iminuit-2.24.0/.ci/download_artifacts.py0000644000000000000000000000234514332717401015103 0ustar00import requests import json import shutil from urllib.request import urlopen from pathlib import Path import zipfile import tempfile organization = "scikit-hep" project = "iminuit" def get(url): return json.loads(requests.get(url).content) def get_latest_successful_build(): # get list of all builds artifact = get( f"https://api.github.com/repos/{organization}/{project}/actions/artifacts" )["artifacts"][0] return artifact["id"], artifact["updated_at"], artifact["archive_download_url"] id, date, url = get_latest_successful_build() # TODO needs authentication to work print(f"Downloading artifacts for {id} {date}") dist = Path("dist") if dist.exists(): if any(dist.iterdir()): raise SystemExit("Error: ./dist directory is not empty; please delete content") else: dist.mkdir() print(f"Downloading {id}...") with tempfile.TemporaryFile(mode="w+b") as tmp: with urlopen(url) as src: shutil.copyfileobj(src, tmp) with zipfile.ZipFile(tmp) as zip: for zip_name in zip.namelist(): fn = dist / Path(zip_name).name print("... extracting", fn) with zip.open(zip_name) as src, open(fn, "wb") as dst: shutil.copyfileobj(src, dst) iminuit-2.24.0/.ci/install_deps.py0000644000000000000000000000111414332717401013706 0ustar00from pathlib import Path import sys def install(packages): import subprocess as subp subp.check_call( ["python", "-m", "pip", "install", "--upgrade", "--prefer-binary"] + packages ) packages = [ "tomli", "pip", "wheel", ] install(packages) import tomli # noqa with open(Path(__file__).parent.parent / "pyproject.toml", "rb") as f: d = tomli.load(f) for arg in sys.argv[1:]: if arg == "build": install(d["build-system"]["requires"]) else: packages = d["project"]["optional-dependencies"][arg] install(packages) iminuit-2.24.0/.ci/release_check.py0000644000000000000000000000242114332717401014004 0ustar00"""Ensure that current version is not in conflict with published releases.""" from pkg_resources import parse_version import subprocess as subp from pathlib import PurePath import urllib.request import json import warnings import sys project_dir = PurePath(__file__).parent.parent changelog_fn = project_dir / "doc/changelog.rst" version_fn = project_dir / "version.py" version = subp.check_output([sys.executable, version_fn]).strip().decode() with warnings.catch_warnings(record=True) as record: iminuit_version = parse_version(version) if record: raise ValueError(record[0].message) print("iminuit version:", iminuit_version) # make sure that changelog was updated with open(changelog_fn) as f: assert iminuit_version.base_version in f.read(), "changelog entry missing" # make sure that version is not already tagged tags = subp.check_output(["git", "tag"]).decode().strip().split("\n") assert f"v{iminuit_version}" not in tags, "tag exists" # make sure that version itself was updated with urllib.request.urlopen("https://pypi.org/pypi/iminuit/json") as r: pypi_versions = [parse_version(v) for v in json.loads(r.read())["releases"]] pypi_versions.sort() print("PyPI version:", pypi_versions[-1]) assert iminuit_version not in pypi_versions, "pypi version exists" iminuit-2.24.0/.ci/root_version.py0000644000000000000000000000102014332717401013751 0ustar00import subprocess as subp from pathlib import PurePath project_dir = PurePath(__file__).parent.parent # check that root version is up-to-date git_submodule = subp.check_output( ["git", "submodule", "status"], cwd=project_dir ).decode() for item in git_submodule.strip().split("\n"): parts = item.split() if PurePath(parts[1]) != PurePath("extern") / "root": continue assert len(parts) == 3, "module is not checked out" this_root_version = parts[2][1:-1] # strip braces print(this_root_version) iminuit-2.24.0/.clang-format0000644000000000000000000000512614332717401012564 0ustar00--- Language: Cpp AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: true AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: true AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: true # BreakInheritanceListBeforeComma: true ColumnLimit: 88 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IndentCaseLabels: true IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 8 UseTab: Never --- Language: Json BasedOnStyle: llvm ... iminuit-2.24.0/.coveragerc0000644000000000000000000000014014332717401012321 0ustar00[run] source = src/iminuit relative_files = True [report] exclude_lines = pragma: no cover iminuit-2.24.0/.github/dependabot.yml0000644000000000000000000000024414332717401014375 0ustar00version: 2 updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" iminuit-2.24.0/.github/workflows/coverage.yml0000644000000000000000000000143214332717401016120 0ustar00name: Coverage env: CMAKE_ARGS: -DCMAKE_CXX_COMPILER_LAUNCHER=ccache # Latest Jupyter requires this to acknowledge deprecation JUPYTER_PLATFORM_DIRS: 1 on: pull_request: paths-ignore: - 'doc/**' - '.ci/**' - '*.rst' push: branches: - main - develop - beta/* paths-ignore: - 'doc/**' - '.ci/**' - '*.rst' jobs: coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: submodules: true - uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }} - uses: actions/setup-python@v4 with: python-version: "3.9" - run: make cov - uses: AndreMiras/coveralls-python-action@develop iminuit-2.24.0/.github/workflows/docs.yml0000644000000000000000000000113314332717401015253 0ustar00name: Docs on: pull_request: paths-ignore: - '.ci/**' env: CMAKE_ARGS: -DCMAKE_CXX_COMPILER_LAUNCHER=ccache # Latest Jupyter requires this to acknowledge deprecation JUPYTER_PLATFORM_DIRS: 1 jobs: docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: submodules: true - uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }} - uses: actions/setup-python@v4 with: python-version: "3.9" - run: sudo apt-get install pandoc - run: make doc iminuit-2.24.0/.github/workflows/release.yml0000644000000000000000000000567214332717401015757 0ustar00name: Release on: push: branches: - main workflow_dispatch: env: # Latest Jupyter requires this to acknowledge deprecation JUPYTER_PLATFORM_DIRS: 1 jobs: release_check: runs-on: ubuntu-latest outputs: tag: ${{ steps.tag.outputs.tag }} steps: - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 - uses: actions/setup-python@v4 with: python-version: '3.11' - if: ${{ github.ref == 'refs/heads/main' }} run: python .ci/release_check.py - id: tag run: echo "tag=$(python version.py)" >> $GITHUB_OUTPUT wheels: needs: release_check name: ${{ matrix.py }} ${{ matrix.os }} ${{ matrix.arch }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] arch: [auto, aarch64, universal2] py: [cp38, cp39, cp310, cp311] exclude: - os: windows-latest arch: aarch64 - os: windows-latest arch: universal2 - os: macos-latest arch: aarch64 - os: macos-latest arch: auto - os: ubuntu-latest arch: universal2 env: CIBW_BUILD: ${{ matrix.py }}-* CIBW_ARCHS_LINUX: ${{ matrix.arch }} steps: - uses: actions/checkout@v3 with: submodules: true - if: ${{ matrix.arch == 'aarch64' }} uses: docker/setup-qemu-action@v2 - uses: pypa/cibuildwheel@v2.14.1 env: CIBW_BUILD: ${{ matrix.py }}-* CIBW_ARCHS: ${{ matrix.arch }} - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl sdist: needs: release_check name: source package runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: submodules: true - run: pipx run build --sdist - run: python -m pip install --upgrade pip setuptools - run: python -m pip install -v $(echo dist/iminuit-*)[test] - run: python -m pytest - uses: actions/upload-artifact@v3 with: path: dist/*.tar.gz upload: needs: [wheels, sdist] runs-on: ubuntu-latest if: ${{ github.ref == 'refs/heads/main' }} steps: - uses: actions/download-artifact@v3 with: name: artifact path: dist - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{secrets.pypi_password}} release: needs: [release_check, upload] runs-on: ubuntu-latest if: ${{ github.ref == 'refs/heads/main' }} steps: - uses: actions/checkout@v3 - uses: softprops/action-gh-release@v1 with: name: v${{ needs.release_check.outputs.tag }} tag_name: v${{ needs.release_check.outputs.tag }} target_commitish: ${{ github.ref_name }} generate_release_notes: true iminuit-2.24.0/.github/workflows/test.yml0000644000000000000000000000422714332717401015311 0ustar00name: Test on: pull_request: paths-ignore: - 'doc/**' - '.ci/**' - '*.rst' env: CMAKE_ARGS: -DCMAKE_CXX_COMPILER_LAUNCHER=ccache # Latest Jupyter requires this to acknowledge deprecation JUPYTER_PLATFORM_DIRS: 1 jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [windows-latest, macos-latest, ubuntu-latest] include: # version number must be string, otherwise 3.10 becomes 3.1 - os: windows-latest python-version: "3.11" - os: macos-latest python-version: "3.8" - os: ubuntu-latest python-version: "pypy-3.8" fail-fast: false steps: - uses: actions/checkout@v3 with: submodules: true # must come after checkout - uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }} - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip wheel # python -m pip install .[test] is not used here to test minimum (faster) - run: python -m pip install -v . pytest - run: python -m pytest # The aarch64 test is very slow, that's why we do not run it # # aarch64: # strategy: # matrix: # py: cp39 # arch: [aarch64] # fail-fast: false # runs-on: ubuntu-latest # env: # py: /opt/python/${{ matrix.py }}-${{ matrix.py }}/bin/python # img: quay.io/pypa/manylinux2014_${{ matrix.arch }} # steps: # - uses: actions/checkout@v2 # with: # submodules: true # - uses: docker/setup-qemu-action@v1 # - run: > # docker run --rm -v ${{ github.workspace }}:/ws:rw --workdir=/ws # ${{ env.img }} # bash -exc '${{ env.py }} -m venv venv && # source venv/bin/activate && # python -m pip install --upgrade pip wheel && # python -m pip install . pytest' # - run: > # docker run --rm -v ${{ github.workspace }}:/ws:rw --workdir=/ws # ${{ env.img }} # venv/bin/python -m pytest iminuit-2.24.0/.gitignore0000644000000000000000000000073314332717401012200 0ustar00/pypy* /py[0-9]* /gammapy /probfit /iminuit-dev /root-* /dev /venv /build /dist /_build /_skbuild __pycache__ *.pyxbldc *.root *.so *.pyc *.o *.pkl pip-wheel-metadata .benchmarks .DS_Store .idea .vscode .cache .project .pydevproject .settings .coverage .ipynb_checkpoints .eggs .pytest_cache .mypy_cache .ruff_cache Untitled*.ipynb Untitled*.py Pipfile iminuit.egg-info archive env MANIFEST htmlcov cover coverage.xml tests/fig/*.svg doc/index.rst testcase *venv* iminuit-2.24.0/.gitmodules0000644000000000000000000000013414332717401012360 0ustar00[submodule "extern/root"] path = extern/root url = https://github.com/HDembinski/root.git iminuit-2.24.0/.pre-commit-config.yaml0000644000000000000000000000304114332717401014464 0ustar00# To use: # # pre-commit run -a # # Or: # # pre-commit install # (runs every time you commit in git) # # To update this file: # # pre-commit autoupdate # # See https://github.com/pre-commit/pre-commit repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-case-conflict - id: check-docstring-first - id: check-executables-have-shebangs - id: check-merge-conflict - id: check-symlinks - id: check-yaml args: ["--allow-multiple-documents"] - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: sort-simple-yaml - id: file-contents-sorter - id: trailing-whitespace exclude: ^doc/_static/.*.svg # Python formatting - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.7.0 hooks: - id: black # Ruff linter, replacement for flake8, pydocstyle, isort - repo: https://github.com/astral-sh/ruff-pre-commit rev: 'v0.0.284' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] # C++ formatting - repo: https://github.com/pre-commit/mirrors-clang-format rev: v16.0.6 hooks: - id: clang-format # CMake formatting - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: - id: cmake-format additional_dependencies: [pyyaml] types: [file] files: (\.cmake|CMakeLists.txt)(.in)?$ # Python type checking - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v1.5.0' hooks: - id: mypy additional_dependencies: [numpy] args: [src] pass_filenames: false iminuit-2.24.0/.readthedocs.yaml0000644000000000000000000000061714332717401013440 0ustar00# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 submodules: include: all sphinx: configuration: doc/conf.py # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.9" python: install: - method: pip path: . extra_requirements: - test - doc system_packages: false iminuit-2.24.0/CITATION.cff0000644000000000000000000000403314332717401012077 0ustar00# This CITATION.cff file was generated with cffinit. # Visit https://bit.ly/cffinit to generate yours today! cff-version: 1.2.0 title: scikit-hep/iminuit message: >- If you use this software, please cite it using the metadata from this file and the MINUIT paper at https://doi.org/10.1016/0010-4655(75)90039-9. This software includes source code under other licenses, see LICENSE in the repository for details. type: software authors: - given-names: Hans family-names: Dembinski email: hans.dembinski@gmail.com affiliation: TU Dortmund orcid: 'https://orcid.org/0000-0003-3337-3850' - given-names: Piti family-names: Ongmongkolkul - given-names: Christoph family-names: Deil - given-names: Henry family-names: Schreiner - given-names: Matthew family-names: Feickert - given-names: Chris family-names: Burr - given-names: Jason family-names: Watson - given-names: Fabian family-names: Rost - given-names: Alex family-names: Pearce - given-names: Lukas family-names: Geiger - given-names: Ahmed family-names: Abdelmotteleb - given-names: Aman family-names: Desai - given-names: Bernhard M. family-names: Wiedemann - given-names: Christoph family-names: Gohlke - given-names: Jeremy family-names: Sanders - given-names: Jonas family-names: Drotleff - given-names: Jonas family-names: Eschle - given-names: Ludwig family-names: Neste - given-names: Marco Edward family-names: Gorelli - given-names: Max family-names: Baak - given-names: Michael family-names: Eliachevitch - given-names: Omar family-names: Zapata identifiers: - type: doi value: 10.5281/zenodo.7695764 repository-code: 'https://github.com/scikit-hep/iminuit' url: 'https://iminuit.readthedocs.io/en/stable/' abstract: >- iminuit is a Jupyter-friendly Python interface for the Minuit2 C++ library maintained by CERN's ROOT team. keywords: - Python - C++ - fitting - optimization - statistics - data analysis - Scikit-HEP license: MIT iminuit-2.24.0/CITATION.rst0000644000000000000000000000216014332717401012150 0ustar00Citation ======== If you use iminuit in a scientific work, please cite us ❤️. A generic BibTeX entry is:: @article{iminuit, author={Hans Dembinski and Piti Ongmongkolkul et al.}, title={scikit-hep/iminuit}, DOI={10.5281/zenodo.3949207}, publisher={Zenodo}, year={2020}, month={Dec}, url={https://doi.org/10.5281/zenodo.3949207} } The DOI and URL in this entry point always to the latest release of iminuit. You can also cite the actual release that you used, please follow the `Zenodo link `_, which offers entries for common bibliography formats for all iminuit releases. The recommended scientific reference for the MINUIT algorithms is:: @article{James:1975dr, author = "James, F. and Roos, M.", title = "{Minuit: A System for Function Minimization and Analysis of the Parameter Errors and Correlations}", reportNumber = "CERN-DD-75-20", doi = "10.1016/0010-4655(75)90039-9", journal = "Comput. Phys. Commun.", volume = "10", pages = "343--367", year = "1975" } iminuit-2.24.0/CMakeLists.txt0000644000000000000000000001032314332717401012744 0ustar00cmake_minimum_required(VERSION 3.15...3.26) project(iminuit LANGUAGES CXX) if(NOT DEFINED SKBUILD) message( WARNING "You should call this through Python so that all variables are set; pip install -v ." ) endif() set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20 set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off find_package(pybind11 CONFIG REQUIRED) file(GLOB SOURCES_A "src/*.cpp") set(SOURCES_B extern/root/math/minuit2/src/AnalyticalGradientCalculator.cxx extern/root/math/minuit2/src/BFGSErrorUpdator.cxx extern/root/math/minuit2/src/DavidonErrorUpdator.cxx extern/root/math/minuit2/src/HessianGradientCalculator.cxx extern/root/math/minuit2/src/ExternalInternalGradientCalculator.cxx extern/root/math/minuit2/src/InitialGradientCalculator.cxx extern/root/math/minuit2/src/LaEigenValues.cxx extern/root/math/minuit2/src/LaInnerProduct.cxx extern/root/math/minuit2/src/LaInverse.cxx extern/root/math/minuit2/src/LaOuterProduct.cxx extern/root/math/minuit2/src/LaSumOfElements.cxx extern/root/math/minuit2/src/LaVtMVSimilarity.cxx extern/root/math/minuit2/src/MPIProcess.cxx extern/root/math/minuit2/src/MinimumBuilder.cxx extern/root/math/minuit2/src/MnApplication.cxx extern/root/math/minuit2/src/MnContours.cxx extern/root/math/minuit2/src/MnCovarianceSqueeze.cxx extern/root/math/minuit2/src/MnFcn.cxx extern/root/math/minuit2/src/MnFunctionCross.cxx extern/root/math/minuit2/src/MnGlobalCorrelationCoeff.cxx extern/root/math/minuit2/src/MnHesse.cxx extern/root/math/minuit2/src/MnLineSearch.cxx extern/root/math/minuit2/src/MnMachinePrecision.cxx extern/root/math/minuit2/src/MnMinos.cxx extern/root/math/minuit2/src/MnParabolaFactory.cxx extern/root/math/minuit2/src/MnParameterScan.cxx extern/root/math/minuit2/src/MnPlot.cxx extern/root/math/minuit2/src/MnPosDef.cxx extern/root/math/minuit2/src/MnPrint.cxx extern/root/math/minuit2/src/MnSeedGenerator.cxx extern/root/math/minuit2/src/MnStrategy.cxx extern/root/math/minuit2/src/MnScan.cxx extern/root/math/minuit2/src/MnTiny.cxx extern/root/math/minuit2/src/MnTraceObject.cxx extern/root/math/minuit2/src/MnUserFcn.cxx extern/root/math/minuit2/src/MnUserParameterState.cxx extern/root/math/minuit2/src/MnUserParameters.cxx extern/root/math/minuit2/src/MnUserTransformation.cxx extern/root/math/minuit2/src/ModularFunctionMinimizer.cxx extern/root/math/minuit2/src/NegativeG2LineSearch.cxx extern/root/math/minuit2/src/Numerical2PGradientCalculator.cxx extern/root/math/minuit2/src/SimplexSeedGenerator.cxx extern/root/math/minuit2/src/SimplexBuilder.cxx extern/root/math/minuit2/src/SimplexParameters.cxx extern/root/math/minuit2/src/SinParameterTransformation.cxx extern/root/math/minuit2/src/ScanBuilder.cxx extern/root/math/minuit2/src/SqrtLowParameterTransformation.cxx extern/root/math/minuit2/src/SqrtUpParameterTransformation.cxx extern/root/math/minuit2/src/VariableMetricBuilder.cxx extern/root/math/minuit2/src/VariableMetricEDMEstimator.cxx extern/root/math/minuit2/src/mnbins.cxx extern/root/math/minuit2/src/mndasum.cxx extern/root/math/minuit2/src/mndaxpy.cxx extern/root/math/minuit2/src/mnddot.cxx extern/root/math/minuit2/src/mndscal.cxx extern/root/math/minuit2/src/mndspmv.cxx extern/root/math/minuit2/src/mndspr.cxx extern/root/math/minuit2/src/mnlsame.cxx extern/root/math/minuit2/src/mnteigen.cxx extern/root/math/minuit2/src/mntplot.cxx extern/root/math/minuit2/src/mnvert.cxx extern/root/math/minuit2/src/mnxerbla.cxx) pybind11_add_module(_core MODULE ${SOURCES_A} ${SOURCES_B}) if(MSVC) target_compile_options(_core PRIVATE /std:c++14 /Y-) else() # lots of warnings from clang and gcc target_compile_options(_core PRIVATE -Wall -Wextra -pedantic -Werror -fstrict-aliasing) endif() target_include_directories(_core PRIVATE extern/root/math/minuit2/inc) set_target_properties(_core PROPERTIES VISIBILITY_INLINES_HIDDEN ON) install(TARGETS _core DESTINATION iminuit) iminuit-2.24.0/CONTRIBUTING.md0000644000000000000000000000017314332717401012437 0ustar00See doc/contribute.rst in this repository or its [html version](https://iminuit.readthedocs.io/en/latest/contribute.html). iminuit-2.24.0/LICENSE0000644000000000000000000000277514332717401011225 0ustar00Minuit is from SEAL Minuit It's LGPL v2 http://seal.web.cern.ch/seal/main/license.html. For iminuit, I'm releasing it as MIT license: Copyright (c) 2012 Piti Ongmongkolkul 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. Note: MIT license is GPL compatible, so it is an acceptable license for a wrapper, as can be seen here: http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.html#GPLWrapper http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.html#OrigBSD (L)GPL can be combined or included in code that does not impose more restrictive conditions. iminuit-2.24.0/Makefile0000644000000000000000000000346214332717401011652 0ustar00# Makefile for developers with some convenient quick ways to do common things # default target build/done: $(wildcard *.py src/*.cpp extern/root/math/minuit2/src/*.cxx extern/root/math/minuit2/inc/*.h) CMakeLists.txt mkdir -p build python .ci/install_deps.py build DEBUG=1 CMAKE_BUILD_PARALLEL_LEVEL=8 CMAKE_ARGS="-DCMAKE_CXX_COMPILER_LAUNCHER=ccache" python -m pip install --no-build-isolation -v -e . touch build/done build/testdep: build/done python .ci/install_deps.py test touch build/testdep test: build/done build/testdep JUPYTER_PLATFORM_DIRS=1 python -m pytest -vv -r a --ff --pdb # this requires that ROOT is installed bench: build/done build/testdep python -m pytest bench --benchmark-autosave # only computes the coverage in pure Python cov: build/done build/testdep rm -rf htmlcov JUPYTER_PLATFORM_DIRS=1 coverage run -m pytest coverage html -d htmlcov coverage report -m @echo htmlcov/index.html doc: build/done build/html/done @echo build/html/index.html build/docdep: build/done python .ci/install_deps.py test doc touch build/docdep build/html/done: build/done build/docdep doc/conf.py $(wildcard src/iminuit/*.py doc/*.rst doc/_static/* doc/plots/* doc/notebooks/*.ipynb doc/notebooks/roofit/*.ipynb *.rst) mkdir -p build/html sphinx-build -v -W -b html -d build/doctrees doc build/html touch build/html/done tutorial: build/done build/tutorial_done build/tutorial_done: $(wildcard src/iminuit/*.py doc/tutorial/*.ipynb) python3 -m pytest -n8 doc/tutorial touch build/tutorial_done check: pre-commit run -a clean: rm -rf build htmlcov src/iminuit/_core* tutorial/.ipynb_checkpoints iminuit.egg-info src/iminuit.egg-info .pytest_cache src/iminuit/__pycache__ tests/__pycache__ tutorial/__pycache__ .coverage .eggs .ipynb_checkpoints dist __pycache__ .PHONY: clean check cov doc test bench iminuit-2.24.0/README.rst0000644000000000000000000001511014332717401011672 0ustar00.. |iminuit| image:: doc/_static/iminuit_logo.svg :alt: iminuit |iminuit| ========= .. version-marker-do-not-remove .. image:: https://scikit-hep.org/assets/images/Scikit--HEP-Project-blue.svg :target: https://scikit-hep.org .. image:: https://img.shields.io/pypi/v/iminuit.svg :target: https://pypi.org/project/iminuit .. image:: https://img.shields.io/conda/vn/conda-forge/iminuit.svg :target: https://github.com/conda-forge/iminuit-feedstock .. image:: https://coveralls.io/repos/github/scikit-hep/iminuit/badge.svg?branch=develop :target: https://coveralls.io/github/scikit-hep/iminuit?branch=develop .. image:: https://readthedocs.org/projects/iminuit/badge/?version=latest :target: https://iminuit.readthedocs.io/en/stable .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3949207.svg :target: https://doi.org/10.5281/zenodo.3949207 .. image:: https://img.shields.io/badge/ascl-2108.024-blue.svg?colorB=262255 :target: https://ascl.net/2108.024 :alt: ascl:2108.024 .. image:: https://img.shields.io/gitter/room/Scikit-HEP/iminuit :target: https://gitter.im/Scikit-HEP/iminuit .. image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/scikit-hep/iminuit/develop?filepath=doc%2Ftutorial *iminuit* is a Jupyter-friendly Python interface for the *Minuit2* C++ library maintained by CERN's ROOT team. Minuit was designed to minimize statistical cost functions, for likelihood and least-squares fits of parametric models to data. It provides the best-fit parameters and error estimates from likelihood profile analysis. The iminuit package comes with additional features: - Builtin cost functions for statistical fits - Binned and unbinned maximum-likelihood - `Template fits with error propagation `_ - Least-squares (optionally robust to outliers) - Gaussian penalty terms for parameters - Cost functions can be combined by adding them: ``total_cost = cost_1 + cost_2`` - Visualization of the fit in Jupyter notebooks - Support for SciPy minimizers as alternatives to Minuit's MIGRAD algorithm (optional) - Support for Numba accelerated functions (optional) Dependencies ------------ *iminuit* is (and always will be) a lean package which only depends on ``numpy``, but additional features are enabled if the following optional packages are installed. - ``matplotlib``: Visualization of fitted model for builtin cost functions - ``ipywidgets``: Interactive fitting, see example below (also requires ``matplotlib``) - ``scipy``: Compute Minos intervals for arbitrary confidence levels - ``unicodeitplus``: Render names of model parameters in simple LaTeX as Unicode Documentation ------------- Checkout our large and comprehensive list of `tutorials`_ that take you all the way from beginner to power user. For help and how-to questions, please use the `discussions`_ on GitHub or `gitter`_. **Lecture by Glen Cowan** `In the exercises to his lecture for the KMISchool 2022 `_, Glen Cowan shows how to solve statistical problems in Python with iminuit. You can find the lectures and exercises on the Github page, which covers both frequentist and Bayesian methods. `Glen Cowan `_ is a known for his papers and international lectures on statistics in particle physics, as a member of the Particle Data Group, and as author of the popular book `Statistical Data Analysis `_. In a nutshell ------------- ``iminuit`` can be used with a user-provided cost functions in form of a negative log-likelihood function or least-squares function. Standard functions are included in ``iminuit.cost``, so you don't have to write them yourself. The following example shows how to perform an unbinned maximum likelihood fit. .. code:: python import numpy as np from iminuit import Minuit from iminuit.cost import UnbinnedNLL from scipy.stats import norm x = norm.rvs(size=1000, random_state=1) def pdf(x, mu, sigma): return norm.pdf(x, mu, sigma) # Negative unbinned log-likelihood, you can write your own cost = UnbinnedNLL(x, pdf) m = Minuit(cost, mu=0, sigma=1) m.limits["sigma"] = (0, np.inf) m.migrad() # find minimum m.hesse() # compute uncertainties .. image:: doc/_static/demo_output.png :alt: Output of the demo in a Jupyter notebook Interactive fitting ------------------- ``iminuit`` optionally supports an interactive fitting mode in Jupyter notebooks. .. image:: doc/_static/interactive_demo.gif :alt: Animated demo of an interactive fit in a Jupyter notebook Faster than RooFit ------------------ When ``iminuit`` is used with cost functions and pdfs that are JIT-compiled with `numba`_ (JIT-compiled pdfs are provided by `numba_stats`_ ), the fit is up to 10x faster compared to an equivalent fit in the `RooFit`_ framework. The gain is particularly large when `numba`_ with auto-parallelization is compared to parallel computation in `RooFit`_. .. image:: doc/_static/roofit_vs_iminuit+numba.svg More information about this benchmark is given `in the Benchmark section of the documentation `_. Partner projects ---------------- * `numba_stats`_ provides faster implementations of probability density functions than scipy, and a few specific ones used in particle physics that are not in scipy. * `boost-histogram`_ from Scikit-HEP provides fast generalized histograms that you can use with the builtin cost functions. * `jacobi`_ provides a robust, fast, and accurate calculation of the Jacobi matrix of any transformation function and building a function for generic error propagation. Versions -------- **The current 2.x series has introduced breaking interfaces changes with respect to the 1.x series.** All interface changes are documented in the `changelog`_ with recommendations how to upgrade. To keep existing scripts running, pin your major iminuit version to <2, i.e. ``pip install 'iminuit<2'`` installs the 1.x series. .. _changelog: https://iminuit.readthedocs.io/en/stable/changelog.html .. _tutorials: https://iminuit.readthedocs.io/en/stable/tutorials.html .. _discussions: https://github.com/scikit-hep/iminuit/discussions .. _gitter: https://gitter.im/Scikit-HEP/iminuit .. _jacobi: https://github.com/hdembinski/jacobi .. _numba_stats: https://github.com/HDembinski/numba-stats .. _boost-histogram: https://github.com/scikit-hep/boost-histogram .. _numba: https://numba.pydata.org .. _RooFit: https://root.cern.ch/doc/master/namespaceRooFit.html iminuit-2.24.0/bench/cost.json0000644000000000000000000063562114332717401013144 0ustar00{ "machine_info": { "node": "MacBook-Pro-2.local", "processor": "i386", "machine": "x86_64", "python_compiler": "Clang 13.0.0 (clang-1300.0.29.3)", "python_implementation": "CPython", "python_implementation_version": "3.8.12", "python_version": "3.8.12", "python_build": [ "default", "Oct 22 2021 18:39:35" ], "release": "21.3.0", "system": "Darwin", "cpu": { "python_version": "3.8.12.final.0 (64 bit)", "cpuinfo_version": [ 8, 0, 0 ], "cpuinfo_version_string": "8.0.0", "arch": "X86_64", "bits": 64, "count": 8, "arch_string_raw": "x86_64", "vendor_id_raw": "GenuineIntel", "brand_raw": "Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz", "hz_advertised_friendly": "2.8000 GHz", "hz_actual_friendly": "2.8000 GHz", "hz_advertised": [ 2800000000, 0 ], "hz_actual": [ 2800000000, 0 ], "l2_cache_size": 262144, "stepping": 10, "model": 142, "family": 6, "flags": [ "1gbpage", "3dnowprefetch", "abm", "acapmsr", "acpi", "adx", "aes", "apic", "avx", "avx1.0", "avx2", "bmi1", "bmi2", "clflush", "clflushopt", "clfsh", "clfsopt", "cmov", "cx16", "cx8", "de", "ds", "ds_cpl", "dscpl", "dtes64", "dts", "em64t", "erms", "est", "f16c", "fma", "fpu", "fpu_csds", "fxsr", "ht", "htt", "ibrs", "intel_pt", "invpcid", "ipt", "l1df", "lahf", "lahf_lm", "lzcnt", "mca", "mce", "mdclear", "mmx", "mon", "monitor", "movbe", "mpx", "msr", "mtrr", "osxsave", "pae", "pat", "pbe", "pcid", "pclmulqdq", "pdcm", "pge", "pni", "popcnt", "prefetchw", "pse", "pse36", "rdrand", "rdrnd", "rdseed", "rdtscp", "rdwrfsgs", "seglim64", "sep", "sgx", "smap", "smep", "ss", "ssbd", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "sse4_1", "sse4_2", "ssse3", "stibp", "syscall", "tm", "tm2", "tpr", "tsc", "tsc_thread_offset", "tscdeadline", "tsci", "tsctmr", "tsxfa", "vme", "vmx", "x2apic", "xd", "xsave", "xtpr" ], "l2_cache_line_size": 256, "l2_cache_associativity": 6 } }, "commit_info": { "id": "5b76d81819cfde26c08db4516f87ad8d5ddc905b", "time": "2022-03-14T18:52:11+01:00", "author_time": "2022-03-14T18:52:11+01:00", "dirty": true, "project": "iminuit", "branch": "develop" }, "benchmarks": [ { "group": null, "name": "test_UnbinnedNLL[False-10]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-10]", "params": { "log": false, "n": 10 }, "param": "False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.613999999987129e-06, "max": 2.6232000000625533e-05, "mean": 6.807627819539115e-06, "stddev": 3.8941006015191716e-06, "rounds": 266, "median": 5.8319999998168726e-06, "iqr": 1.0900000102509466e-07, "q1": 5.782999999759397e-06, "q3": 5.892000000784492e-06, "iqr_outliers": 29, "stddev_outliers": 15, "outliers": "15;29", "ld15iqr": 5.6440000006929836e-06, "hd15iqr": 6.056000000143058e-06, "ops": 146894.04687045614, "total": 0.0018108289999974048, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-31]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-31]", "params": { "log": false, "n": 31 }, "param": "False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.01999999982894e-06, "max": 0.00012303500000054868, "mean": 6.816884017796628e-06, "stddev": 2.323222675723695e-06, "rounds": 44731, "median": 6.545000000635071e-06, "iqr": 3.320000008244506e-07, "q1": 6.399999999295858e-06, "q3": 6.732000000120308e-06, "iqr_outliers": 1712, "stddev_outliers": 690, "outliers": "690;1712", "ld15iqr": 6.01999999982894e-06, "hd15iqr": 7.231999999746108e-06, "ops": 146694.58910982363, "total": 0.304926039000061, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-100]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-100]", "params": { "log": false, "n": 100 }, "param": "False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 7.054000000117355e-06, "max": 0.00016630500000047732, "mean": 8.018752700972623e-06, "stddev": 3.2906121861149325e-06, "rounds": 45354, "median": 7.604999999522022e-06, "iqr": 3.2199999999704687e-07, "q1": 7.461000000041906e-06, "q3": 7.783000000038953e-06, "iqr_outliers": 2576, "stddev_outliers": 880, "outliers": "880;2576", "ld15iqr": 7.054000000117355e-06, "hd15iqr": 8.267000000117264e-06, "ops": 124707.67428439418, "total": 0.36368250999991236, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-316]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-316]", "params": { "log": false, "n": 316 }, "param": "False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.546000000248966e-06, "max": 0.00017028099999993884, "mean": 1.109931439856433e-05, "stddev": 4.326686668004807e-06, "rounds": 38483, "median": 1.0506999999826405e-05, "iqr": 6.220000008383408e-07, "q1": 1.0136999999410534e-05, "q3": 1.0759000000248875e-05, "iqr_outliers": 1857, "stddev_outliers": 1125, "outliers": "1125;1857", "ld15iqr": 9.546000000248966e-06, "hd15iqr": 1.1693999999451421e-05, "ops": 90095.65492886187, "total": 0.4271349159999511, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-1000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-1000]", "params": { "log": false, "n": 1000 }, "param": "False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.6870999999696323e-05, "max": 0.00020322700000008354, "mean": 2.1278980280460123e-05, "stddev": 1.0796045995011676e-05, "rounds": 15974, "median": 1.8531000000265863e-05, "iqr": 1.0599999997751297e-06, "q1": 1.794599999982438e-05, "q3": 1.900599999959951e-05, "iqr_outliers": 2115, "stddev_outliers": 1376, "outliers": "1376;2115", "ld15iqr": 1.6870999999696323e-05, "hd15iqr": 2.060899999989374e-05, "ops": 46994.73315073614, "total": 0.33991043100007, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-3162]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-3162]", "params": { "log": false, "n": 3162 }, "param": "False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 3.918599999952477e-05, "max": 0.000341052999999647, "mean": 5.476941792159598e-05, "stddev": 2.465966861807974e-05, "rounds": 16070, "median": 4.4873000000222873e-05, "iqr": 8.62499999954025e-06, "q1": 4.2888000000296245e-05, "q3": 5.1512999999836495e-05, "iqr_outliers": 2870, "stddev_outliers": 2113, "outliers": "2113;2870", "ld15iqr": 3.918599999952477e-05, "hd15iqr": 6.445400000032464e-05, "ops": 18258.36457549228, "total": 0.8801445460000474, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-10000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-10000]", "params": { "log": false, "n": 10000 }, "param": "False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00012126399999967674, "max": 0.0005189279999999741, "mean": 0.0001688479985113522, "stddev": 5.476428721666543e-05, "rounds": 2687, "median": 0.0001410760000002398, "iqr": 5.87212500005041e-05, "q1": 0.00013086899999947832, "q3": 0.00018959024999998242, "iqr_outliers": 155, "stddev_outliers": 413, "outliers": "413;155", "ld15iqr": 0.00012126399999967674, "hd15iqr": 0.0002777659999999571, "ops": 5922.486548946369, "total": 0.4536945720000034, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-31622]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-31622]", "params": { "log": false, "n": 31622 }, "param": "False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00036192599999917974, "max": 0.0010299439999998938, "mean": 0.00047745339502761915, "stddev": 0.0001255235368462855, "rounds": 1086, "median": 0.0004199184999995609, "iqr": 0.00013283600000058016, "q1": 0.0003908599999995488, "q3": 0.000523696000000129, "iqr_outliers": 85, "stddev_outliers": 168, "outliers": "168;85", "ld15iqr": 0.00036192599999917974, "hd15iqr": 0.0007232020000005335, "ops": 2094.445259818821, "total": 0.5185143869999944, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-100000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-100000]", "params": { "log": false, "n": 100000 }, "param": "False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0011009369999985807, "max": 0.002529974999999851, "mean": 0.001402640759219157, "stddev": 0.00030394511879178475, "rounds": 461, "median": 0.0012860190000001381, "iqr": 0.00032229700000208084, "q1": 0.001183275749998991, "q3": 0.0015055727500010718, "iqr_outliers": 32, "stddev_outliers": 73, "outliers": "73;32", "ld15iqr": 0.0011009369999985807, "hd15iqr": 0.00200464300000025, "ops": 712.940924771568, "total": 0.6466173900000314, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-316227]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-316227]", "params": { "log": false, "n": 316227 }, "param": "False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004103880000000615, "max": 0.00699744199999941, "mean": 0.004840556282258055, "stddev": 0.00042142557346933887, "rounds": 124, "median": 0.0048290335000000795, "iqr": 0.0005937444999997155, "q1": 0.0045202749999999625, "q3": 0.005114019499999678, "iqr_outliers": 1, "stddev_outliers": 41, "outliers": "41;1", "ld15iqr": 0.004103880000000615, "hd15iqr": 0.00699744199999941, "ops": 206.5878262102374, "total": 0.6002289789999988, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[False-1000000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[False-1000000]", "params": { "log": false, "n": 1000000 }, "param": "False-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.02014899499999956, "max": 0.03069126699999991, "mean": 0.02306327500000005, "stddev": 0.0031518522647303528, "rounds": 32, "median": 0.02149305200000029, "iqr": 0.00429467100000025, "q1": 0.02059766350000025, "q3": 0.0248923345000005, "iqr_outliers": 0, "stddev_outliers": 7, "outliers": "7;0", "ld15iqr": 0.02014899499999956, "hd15iqr": 0.03069126699999991, "ops": 43.35897655471731, "total": 0.7380248000000016, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-10]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-10]", "params": { "log": true, "n": 10 }, "param": "True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.861000000555009e-06, "max": 0.00011064799999971342, "mean": 1.2904325369988345e-05, "stddev": 6.2280154244536975e-06, "rounds": 9460, "median": 1.1257000000597372e-05, "iqr": 8.969999996111255e-07, "q1": 1.0861499999847979e-05, "q3": 1.1758499999459104e-05, "iqr_outliers": 1496, "stddev_outliers": 734, "outliers": "734;1496", "ld15iqr": 9.861000000555009e-06, "hd15iqr": 1.3104999998958533e-05, "ops": 77493.39630924877, "total": 0.12207491800008974, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-31]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-31]", "params": { "log": true, "n": 31 }, "param": "True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.0087999999797148e-05, "max": 0.0008933570000007052, "mean": 1.4521875886642965e-05, "stddev": 1.048082927260062e-05, "rounds": 31439, "median": 1.158200000084264e-05, "iqr": 1.1149999994408688e-06, "q1": 1.1069999999779867e-05, "q3": 1.2184999999220736e-05, "iqr_outliers": 6311, "stddev_outliers": 2651, "outliers": "2651;6311", "ld15iqr": 1.0087999999797148e-05, "hd15iqr": 1.3857999999089543e-05, "ops": 68861.62695549459, "total": 0.45655325600016816, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-100]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-100]", "params": { "log": true, "n": 100 }, "param": "True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.0066999999835957e-05, "max": 0.00014689099999998234, "mean": 1.3911768780707706e-05, "stddev": 7.284360644200518e-06, "rounds": 21365, "median": 1.1793999998843674e-05, "iqr": 1.0229999993782712e-06, "q1": 1.1591999999893687e-05, "q3": 1.2614999999271959e-05, "iqr_outliers": 3185, "stddev_outliers": 1637, "outliers": "1637;3185", "ld15iqr": 1.0066999999835957e-05, "hd15iqr": 1.4150000000157092e-05, "ops": 71881.58571085229, "total": 0.29722493999982014, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-316]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-316]", "params": { "log": true, "n": 316 }, "param": "True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.1033000001603455e-05, "max": 0.00014430300000078944, "mean": 1.517401692263753e-05, "stddev": 7.712991478504646e-06, "rounds": 21096, "median": 1.2793000001565247e-05, "iqr": 1.4230000004999965e-06, "q1": 1.2477999999482847e-05, "q3": 1.3900999999982844e-05, "iqr_outliers": 3203, "stddev_outliers": 1738, "outliers": "1738;3203", "ld15iqr": 1.1033000001603455e-05, "hd15iqr": 1.603599999988603e-05, "ops": 65902.12763689083, "total": 0.32011106099996134, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-1000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-1000]", "params": { "log": true, "n": 1000 }, "param": "True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.186399999930643e-05, "max": 0.0001897890000002178, "mean": 1.5743009087816723e-05, "stddev": 7.027698432300104e-06, "rounds": 17606, "median": 1.3716999999857649e-05, "iqr": 1.3730000016920485e-06, "q1": 1.3452999999330473e-05, "q3": 1.4826000001022521e-05, "iqr_outliers": 1916, "stddev_outliers": 1396, "outliers": "1396;1916", "ld15iqr": 1.186399999930643e-05, "hd15iqr": 1.688700000102017e-05, "ops": 63520.25806641278, "total": 0.27717141800010126, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-3162]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-3162]", "params": { "log": true, "n": 3162 }, "param": "True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.5538999999620273e-05, "max": 0.00014090600000038478, "mean": 1.96098519289649e-05, "stddev": 7.033629237581259e-06, "rounds": 16330, "median": 1.7546000000479012e-05, "iqr": 1.7870000004194253e-06, "q1": 1.715600000018469e-05, "q3": 1.8943000000604115e-05, "iqr_outliers": 1613, "stddev_outliers": 1318, "outliers": "1318;1613", "ld15iqr": 1.5538999999620273e-05, "hd15iqr": 2.1632000001048368e-05, "ops": 50994.775667986636, "total": 0.3202288819999968, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-10000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-10000]", "params": { "log": true, "n": 10000 }, "param": "True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.8184999999680826e-05, "max": 0.00013907000000124015, "mean": 3.572925095727153e-05, "stddev": 1.084553273841856e-05, "rounds": 3395, "median": 3.183599999978526e-05, "iqr": 3.561500000248685e-06, "q1": 3.0721249999920985e-05, "q3": 3.428275000016967e-05, "iqr_outliers": 537, "stddev_outliers": 385, "outliers": "385;537", "ld15iqr": 2.8184999999680826e-05, "hd15iqr": 3.9652000001666465e-05, "ops": 27988.272163776848, "total": 0.12130080699993684, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-31622]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-31622]", "params": { "log": true, "n": 31622 }, "param": "True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.362099999890347e-05, "max": 0.00022118799999937266, "mean": 8.053470712206149e-05, "stddev": 2.237386259623731e-05, "rounds": 1376, "median": 7.234949999901374e-05, "iqr": 1.333300000005977e-05, "q1": 6.82505000000333e-05, "q3": 8.158350000009307e-05, "iqr_outliers": 173, "stddev_outliers": 170, "outliers": "170;173", "ld15iqr": 6.362099999890347e-05, "hd15iqr": 0.00010197200000128248, "ops": 12417.00672586245, "total": 0.1108157569999566, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-100000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-100000]", "params": { "log": true, "n": 100000 }, "param": "True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00017283899999931407, "max": 0.0006448110000008, "mean": 0.0002660068870056153, "stddev": 8.887120161713912e-05, "rounds": 354, "median": 0.00023938700000059043, "iqr": 0.00010831899999885763, "q1": 0.0001986719999997888, "q3": 0.00030699099999864643, "iqr_outliers": 14, "stddev_outliers": 60, "outliers": "60;14", "ld15iqr": 0.00017283899999931407, "hd15iqr": 0.000481723000000045, "ops": 3759.301164179597, "total": 0.0941664379999878, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-316227]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-316227]", "params": { "log": true, "n": 316227 }, "param": "True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007201549999997781, "max": 0.0021617169999998964, "mean": 0.0010889014239999569, "stddev": 0.0002952186780363007, "rounds": 125, "median": 0.0010181800000008678, "iqr": 0.0003412147499983753, "q1": 0.0008715085000003953, "q3": 0.0012127232499987706, "iqr_outliers": 6, "stddev_outliers": 27, "outliers": "27;6", "ld15iqr": 0.0007201549999997781, "hd15iqr": 0.0017415899999999596, "ops": 918.3567749655543, "total": 0.1361126779999946, "iterations": 1 } }, { "group": null, "name": "test_UnbinnedNLL[True-1000000]", "fullname": "bench/test_cost.py::test_UnbinnedNLL[True-1000000]", "params": { "log": true, "n": 1000000 }, "param": "True-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.006835566999999543, "max": 0.015650539000001018, "mean": 0.00975040879411762, "stddev": 0.0021127750091034783, "rounds": 34, "median": 0.009763373000000186, "iqr": 0.002199320999999088, "q1": 0.008179027000000616, "q3": 0.010378347999999704, "iqr_outliers": 2, "stddev_outliers": 11, "outliers": "11;2", "ld15iqr": 0.006835566999999543, "hd15iqr": 0.015237973000001404, "ops": 102.55980247754287, "total": 0.33151389899999906, "iterations": 1 } }, { "group": null, "name": "test_custom[False-10]", "fullname": "bench/test_cost.py::test_custom[False-10]", "params": { "log": false, "n": 10 }, "param": "False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.966000000360964e-06, "max": 0.00024564900000001444, "mean": 1.3333629878563186e-05, "stddev": 8.059901903783004e-06, "rounds": 17705, "median": 1.1383000000364518e-05, "iqr": 7.669999995130183e-07, "q1": 1.0903999999811731e-05, "q3": 1.167099999932475e-05, "iqr_outliers": 2760, "stddev_outliers": 1442, "outliers": "1442;2760", "ld15iqr": 9.966000000360964e-06, "hd15iqr": 1.2824999998883868e-05, "ops": 74998.33197018054, "total": 0.23607191699996122, "iterations": 1 } }, { "group": null, "name": "test_custom[False-31]", "fullname": "bench/test_cost.py::test_custom[False-31]", "params": { "log": false, "n": 31 }, "param": "False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.0072000000249659e-05, "max": 0.00015143200000089507, "mean": 1.2920464623972505e-05, "stddev": 6.0667386601100956e-06, "rounds": 28296, "median": 1.1435999999420687e-05, "iqr": 1.1170000000504388e-06, "q1": 1.0959999999116121e-05, "q3": 1.207699999916656e-05, "iqr_outliers": 3148, "stddev_outliers": 1756, "outliers": "1756;3148", "ld15iqr": 1.0072000000249659e-05, "hd15iqr": 1.375699999961455e-05, "ops": 77396.59749887088, "total": 0.365597466999926, "iterations": 1 } }, { "group": null, "name": "test_custom[False-100]", "fullname": "bench/test_cost.py::test_custom[False-100]", "params": { "log": false, "n": 100 }, "param": "False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.1143000000046754e-05, "max": 0.00018102599999991753, "mean": 1.3956544709495123e-05, "stddev": 8.850763497969071e-06, "rounds": 27332, "median": 1.1990999999156315e-05, "iqr": 4.919999998520552e-07, "q1": 1.1842999999345238e-05, "q3": 1.2334999999197294e-05, "iqr_outliers": 2873, "stddev_outliers": 1668, "outliers": "1668;2873", "ld15iqr": 1.1143000000046754e-05, "hd15iqr": 1.3072999999863555e-05, "ops": 71650.97241580613, "total": 0.3814602799999207, "iterations": 1 } }, { "group": null, "name": "test_custom[False-316]", "fullname": "bench/test_cost.py::test_custom[False-316]", "params": { "log": false, "n": 316 }, "param": "False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.4291999999471727e-05, "max": 0.0003033289999994082, "mean": 1.824192360940663e-05, "stddev": 1.128592405055234e-05, "rounds": 20369, "median": 1.542300000068053e-05, "iqr": 5.450000011286704e-07, "q1": 1.5228000000089281e-05, "q3": 1.577300000121795e-05, "iqr_outliers": 3139, "stddev_outliers": 1608, "outliers": "1608;3139", "ld15iqr": 1.4411000000436047e-05, "hd15iqr": 1.659399999986988e-05, "ops": 54818.7801578305, "total": 0.3715697420000037, "iterations": 1 } }, { "group": null, "name": "test_custom[False-1000]", "fullname": "bench/test_cost.py::test_custom[False-1000]", "params": { "log": false, "n": 1000 }, "param": "False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.0483999998432978e-05, "max": 0.0006008140000002271, "mean": 2.8404916538409974e-05, "stddev": 1.940288907234837e-05, "rounds": 20141, "median": 2.2700999998903626e-05, "iqr": 1.0439999993394622e-06, "q1": 2.248500000057163e-05, "q3": 2.3528999999911093e-05, "iqr_outliers": 4157, "stddev_outliers": 1463, "outliers": "1463;4157", "ld15iqr": 2.095600000018294e-05, "hd15iqr": 2.5103000000470388e-05, "ops": 35205.17297235393, "total": 0.5721034240001153, "iterations": 1 } }, { "group": null, "name": "test_custom[False-3162]", "fullname": "bench/test_cost.py::test_custom[False-3162]", "params": { "log": false, "n": 3162 }, "param": "False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 4.402500000111331e-05, "max": 0.0010989180000002818, "mean": 6.062804127025641e-05, "stddev": 4.098725536298938e-05, "rounds": 7778, "median": 4.5823000000666525e-05, "iqr": 1.1557000000550488e-05, "q1": 4.53880000002016e-05, "q3": 5.694500000075209e-05, "iqr_outliers": 1255, "stddev_outliers": 716, "outliers": "716;1255", "ld15iqr": 4.402500000111331e-05, "hd15iqr": 7.437300000034952e-05, "ops": 16494.017933754218, "total": 0.4715649050000543, "iterations": 1 } }, { "group": null, "name": "test_custom[False-10000]", "fullname": "bench/test_cost.py::test_custom[False-10000]", "params": { "log": false, "n": 10000 }, "param": "False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00011805799999997646, "max": 0.000288777999999823, "mean": 0.00014026708187140824, "stddev": 3.206076021385848e-05, "rounds": 171, "median": 0.00012519699999913314, "iqr": 2.349775000087817e-05, "q1": 0.00012187674999974973, "q3": 0.0001453745000006279, "iqr_outliers": 19, "stddev_outliers": 24, "outliers": "24;19", "ld15iqr": 0.00011805799999997646, "hd15iqr": 0.00018273300000082315, "ops": 7129.256463157646, "total": 0.02398567100001081, "iterations": 1 } }, { "group": null, "name": "test_custom[False-31622]", "fullname": "bench/test_cost.py::test_custom[False-31622]", "params": { "log": false, "n": 31622 }, "param": "False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0003327609999992376, "max": 0.0011477549999998615, "mean": 0.0003945496537657045, "stddev": 8.637925549902671e-05, "rounds": 956, "median": 0.0003578080000004036, "iqr": 4.9915000000844145e-05, "q1": 0.00034959099999998244, "q3": 0.0003995060000008266, "iqr_outliers": 109, "stddev_outliers": 104, "outliers": "104;109", "ld15iqr": 0.0003327609999992376, "hd15iqr": 0.0004750000000015575, "ops": 2534.535236454244, "total": 0.3771894690000135, "iterations": 1 } }, { "group": null, "name": "test_custom[False-100000]", "fullname": "bench/test_cost.py::test_custom[False-100000]", "params": { "log": false, "n": 100000 }, "param": "False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0010441270000001168, "max": 0.0024914440000003424, "mean": 0.0013609152873899988, "stddev": 0.0003018179004937743, "rounds": 341, "median": 0.0012594489999990799, "iqr": 0.00035873650000262103, "q1": 0.0011328457499990563, "q3": 0.0014915822500016773, "iqr_outliers": 19, "stddev_outliers": 51, "outliers": "51;19", "ld15iqr": 0.0010441270000001168, "hd15iqr": 0.0020326839999995627, "ops": 734.7995935278439, "total": 0.4640721129999896, "iterations": 1 } }, { "group": null, "name": "test_custom[False-316227]", "fullname": "bench/test_cost.py::test_custom[False-316227]", "params": { "log": false, "n": 316227 }, "param": "False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0037355100000020514, "max": 0.011600158000000249, "mean": 0.005870520244680857, "stddev": 0.0019264995299903547, "rounds": 94, "median": 0.0053474695000002015, "iqr": 0.00284201499999881, "q1": 0.004240069000001512, "q3": 0.007082084000000322, "iqr_outliers": 1, "stddev_outliers": 25, "outliers": "25;1", "ld15iqr": 0.0037355100000020514, "hd15iqr": 0.011600158000000249, "ops": 170.3426541976543, "total": 0.5518289030000005, "iterations": 1 } }, { "group": null, "name": "test_custom[False-1000000]", "fullname": "bench/test_cost.py::test_custom[False-1000000]", "params": { "log": false, "n": 1000000 }, "param": "False-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.01992837400000269, "max": 0.030484014999998976, "mean": 0.023060005529411728, "stddev": 0.0026836474675817536, "rounds": 34, "median": 0.022698909500000752, "iqr": 0.0037107550000001766, "q1": 0.020900592000000273, "q3": 0.02461134700000045, "iqr_outliers": 1, "stddev_outliers": 8, "outliers": "8;1", "ld15iqr": 0.01992837400000269, "hd15iqr": 0.030484014999998976, "ops": 43.36512403366759, "total": 0.7840401879999988, "iterations": 1 } }, { "group": null, "name": "test_custom[True-10]", "fullname": "bench/test_cost.py::test_custom[True-10]", "params": { "log": true, "n": 10 }, "param": "True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 8.789999998981557e-06, "max": 0.0007441660000004902, "mean": 1.3133151607740057e-05, "stddev": 1.2985262274528562e-05, "rounds": 21272, "median": 9.6799999980135e-06, "iqr": 5.479999991564455e-07, "q1": 9.512000001876686e-06, "q3": 1.0060000001033131e-05, "iqr_outliers": 3515, "stddev_outliers": 1608, "outliers": "1608;3515", "ld15iqr": 8.789999998981557e-06, "hd15iqr": 1.0882999998074183e-05, "ops": 76143.18557098262, "total": 0.2793684009998465, "iterations": 1 } }, { "group": null, "name": "test_custom[True-31]", "fullname": "bench/test_cost.py::test_custom[True-31]", "params": { "log": true, "n": 31 }, "param": "True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.032000001241158e-06, "max": 0.000202774000001682, "mean": 1.2148806264562728e-05, "stddev": 9.895840143008478e-06, "rounds": 31734, "median": 9.932000001100505e-06, "iqr": 4.620000026989146e-07, "q1": 9.676999997765279e-06, "q3": 1.0139000000464193e-05, "iqr_outliers": 3591, "stddev_outliers": 2027, "outliers": "2027;3591", "ld15iqr": 9.032000001241158e-06, "hd15iqr": 1.0833000001042592e-05, "ops": 82312.61394931735, "total": 0.3855302179996336, "iterations": 1 } }, { "group": null, "name": "test_custom[True-100]", "fullname": "bench/test_cost.py::test_custom[True-100]", "params": { "log": true, "n": 100 }, "param": "True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.170999998531215e-06, "max": 0.0002294269999971732, "mean": 1.2380379749494696e-05, "stddev": 1.0329272028596635e-05, "rounds": 33372, "median": 9.99099999887676e-06, "iqr": 4.6000000253343387e-07, "q1": 9.734999999011507e-06, "q3": 1.019500000154494e-05, "iqr_outliers": 4550, "stddev_outliers": 2308, "outliers": "2308;4550", "ld15iqr": 9.170999998531215e-06, "hd15iqr": 1.0885999998322404e-05, "ops": 80772.96659989892, "total": 0.413158033000137, "iterations": 1 } }, { "group": null, "name": "test_custom[True-316]", "fullname": "bench/test_cost.py::test_custom[True-316]", "params": { "log": true, "n": 316 }, "param": "True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.731000002233259e-06, "max": 0.00029858699999962823, "mean": 1.367896212417522e-05, "stddev": 1.0813466337242634e-05, "rounds": 25478, "median": 1.0877999997660481e-05, "iqr": 7.020000012403216e-07, "q1": 1.0460999998684883e-05, "q3": 1.1162999999925205e-05, "iqr_outliers": 4276, "stddev_outliers": 2161, "outliers": "2161;4276", "ld15iqr": 9.731000002233259e-06, "hd15iqr": 1.2219000002033908e-05, "ops": 73104.96154036946, "total": 0.34851259699973625, "iterations": 1 } }, { "group": null, "name": "test_custom[True-1000]", "fullname": "bench/test_cost.py::test_custom[True-1000]", "params": { "log": true, "n": 1000 }, "param": "True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.1400999998301131e-05, "max": 0.0003645020000000443, "mean": 1.8334365914677316e-05, "stddev": 1.4686574395030505e-05, "rounds": 21986, "median": 1.2191999999799918e-05, "iqr": 6.534000000613105e-06, "q1": 1.1879000002323892e-05, "q3": 1.8413000002936997e-05, "iqr_outliers": 2787, "stddev_outliers": 1954, "outliers": "1954;2787", "ld15iqr": 1.1400999998301131e-05, "hd15iqr": 2.8219000000717642e-05, "ops": 54542.38257563433, "total": 0.40309936900009546, "iterations": 1 } }, { "group": null, "name": "test_custom[True-3162]", "fullname": "bench/test_cost.py::test_custom[True-3162]", "params": { "log": true, "n": 3162 }, "param": "True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.4678000002987801e-05, "max": 0.00021816099999938388, "mean": 2.0741297327134935e-05, "stddev": 1.5083305725205552e-05, "rounds": 18108, "median": 1.5502000000111593e-05, "iqr": 1.0165000023931725e-06, "q1": 1.5300999997691633e-05, "q3": 1.6317500000084806e-05, "iqr_outliers": 3633, "stddev_outliers": 1610, "outliers": "1610;3633", "ld15iqr": 1.4678000002987801e-05, "hd15iqr": 1.784299999840755e-05, "ops": 48212.99189861878, "total": 0.3755834119997594, "iterations": 1 } }, { "group": null, "name": "test_custom[True-10000]", "fullname": "bench/test_cost.py::test_custom[True-10000]", "params": { "log": true, "n": 10000 }, "param": "True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.8541999999021073e-05, "max": 0.0002257589999992149, "mean": 3.8309295480477375e-05, "stddev": 2.1328575645327324e-05, "rounds": 2633, "median": 2.947999999847184e-05, "iqr": 4.855249998492184e-06, "q1": 2.9189000001927923e-05, "q3": 3.4044250000420107e-05, "iqr_outliers": 555, "stddev_outliers": 267, "outliers": "267;555", "ld15iqr": 2.8541999999021073e-05, "hd15iqr": 4.1689999999761085e-05, "ops": 26103.325249340734, "total": 0.10086837500009693, "iterations": 1 } }, { "group": null, "name": "test_custom[True-31622]", "fullname": "bench/test_cost.py::test_custom[True-31622]", "params": { "log": true, "n": 31622 }, "param": "True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.303400000007287e-05, "max": 0.0003410009999988972, "mean": 7.764404535792864e-05, "stddev": 3.0996005951667394e-05, "rounds": 1411, "median": 6.483900000020526e-05, "iqr": 3.5792500030495944e-06, "q1": 6.404674999860305e-05, "q3": 6.762600000165264e-05, "iqr_outliers": 307, "stddev_outliers": 170, "outliers": "170;307", "ld15iqr": 6.303400000007287e-05, "hd15iqr": 7.309700000135422e-05, "ops": 12879.287721165661, "total": 0.10955574800003731, "iterations": 1 } }, { "group": null, "name": "test_custom[True-100000]", "fullname": "bench/test_cost.py::test_custom[True-100000]", "params": { "log": true, "n": 100000 }, "param": "True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00017040299999848685, "max": 0.0005319950000028939, "mean": 0.00021622023185481127, "stddev": 6.987675584364205e-05, "rounds": 496, "median": 0.00017799749999980463, "iqr": 6.552499999834538e-05, "q1": 0.00017415550000166036, "q3": 0.00023968050000000574, "iqr_outliers": 41, "stddev_outliers": 69, "outliers": "69;41", "ld15iqr": 0.00017040299999848685, "hd15iqr": 0.00034965300000067145, "ops": 4624.914104575955, "total": 0.1072452349999864, "iterations": 1 } }, { "group": null, "name": "test_custom[True-316227]", "fullname": "bench/test_cost.py::test_custom[True-316227]", "params": { "log": true, "n": 316227 }, "param": "True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007048860000011814, "max": 0.0013792859999988138, "mean": 0.0008437566850828471, "stddev": 0.00012339195355524246, "rounds": 181, "median": 0.0007973080000027721, "iqr": 0.00012779849999944304, "q1": 0.0007587457500006778, "q3": 0.0008865442500001208, "iqr_outliers": 13, "stddev_outliers": 34, "outliers": "34;13", "ld15iqr": 0.0007048860000011814, "hd15iqr": 0.0010797920000022998, "ops": 1185.1757949648857, "total": 0.15271995999999533, "iterations": 1 } }, { "group": null, "name": "test_custom[True-1000000]", "fullname": "bench/test_cost.py::test_custom[True-1000000]", "params": { "log": true, "n": 1000000 }, "param": "True-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.007021717000000649, "max": 0.010763501999999647, "mean": 0.008429458762711945, "stddev": 0.0008034515838174789, "rounds": 59, "median": 0.008447413999999043, "iqr": 0.0012559467500015131, "q1": 0.007796427249999738, "q3": 0.009052374000001251, "iqr_outliers": 0, "stddev_outliers": 20, "outliers": "20;0", "ld15iqr": 0.007021717000000649, "hd15iqr": 0.010763501999999647, "ops": 118.63157862798272, "total": 0.4973380670000047, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-10]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-10]", "params": { "fastmath": false, "parallel": false, "n": 10 }, "param": "False-False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.569999999011088e-07, "max": 9.442200000009393e-05, "mean": 7.384942069935835e-07, "stddev": 1.3178564893478023e-06, "rounds": 151562, "median": 6.510000005732763e-07, "iqr": 4.4000000087862645e-08, "q1": 6.270000021402211e-07, "q3": 6.710000022280838e-07, "iqr_outliers": 6690, "stddev_outliers": 904, "outliers": "904;6690", "ld15iqr": 5.620000003148107e-07, "hd15iqr": 7.379999971135476e-07, "ops": 1354106.7628289312, "total": 0.1119276590003615, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-31]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-31]", "params": { "fastmath": false, "parallel": false, "n": 31 }, "param": "False-False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.08050000039384e-07, "max": 8.525150000160409e-06, "mean": 7.301177386555848e-07, "stddev": 3.304085264139116e-07, "rounds": 75234, "median": 6.574500000411376e-07, "iqr": 3.0949999896279223e-08, "q1": 6.448500000644231e-07, "q3": 6.757999999607023e-07, "iqr_outliers": 6112, "stddev_outliers": 4451, "outliers": "4451;6112", "ld15iqr": 6.08050000039384e-07, "hd15iqr": 7.222499998960075e-07, "ops": 1369642.1098347555, "total": 0.0549296779500134, "iterations": 20 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-100]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-100]", "params": { "fastmath": false, "parallel": false, "n": 100 }, "param": "False-False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.763500000062095e-07, "max": 1.0546700000091392e-05, "mean": 8.538209473740091e-07, "stddev": 4.178100397458519e-07, "rounds": 62953, "median": 7.458000000681864e-07, "iqr": 5.410000003536197e-08, "q1": 7.22100000061232e-07, "q3": 7.76200000096594e-07, "iqr_outliers": 6993, "stddev_outliers": 5168, "outliers": "5168;6993", "ld15iqr": 6.763500000062095e-07, "hd15iqr": 8.574499998914575e-07, "ops": 1171205.7464455054, "total": 0.053750590100036806, "iterations": 20 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-316]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-316]", "params": { "fastmath": false, "parallel": false, "n": 316 }, "param": "False-False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 8.770000000879463e-07, "max": 4.2351599999790325e-05, "mean": 1.135184090152709e-06, "stddev": 8.640068702973347e-07, "rounds": 195313, "median": 9.75399999703086e-07, "iqr": 5.05999999234064e-08, "q1": 9.439999999472093e-07, "q3": 9.945999998706157e-07, "iqr_outliers": 25154, "stddev_outliers": 6912, "outliers": "6912;25154", "ld15iqr": 8.770000000879463e-07, "hd15iqr": 1.0705999997639992e-06, "ops": 880914.3897226823, "total": 0.2217162102000466, "iterations": 5 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-1000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-1000]", "params": { "fastmath": false, "parallel": false, "n": 1000 }, "param": "False-False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.934000000147762e-06, "max": 0.00015526800000031926, "mean": 2.591447944351279e-06, "stddev": 3.1300665431034445e-06, "rounds": 197942, "median": 2.162000001248998e-06, "iqr": 9.599999728493458e-08, "q1": 2.1100000004992125e-06, "q3": 2.205999997784147e-06, "iqr_outliers": 25596, "stddev_outliers": 4300, "outliers": "4300;25596", "ld15iqr": 1.9669999993254805e-06, "hd15iqr": 2.3499999990406195e-06, "ops": 385884.65656034293, "total": 0.5129563890007809, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-3162]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-3162]", "params": { "fastmath": false, "parallel": false, "n": 3162 }, "param": "False-False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 4.73999999783814e-06, "max": 0.00010552999999902113, "mean": 5.680130016329278e-06, "stddev": 3.55400793831634e-06, "rounds": 121877, "median": 5.089000001845534e-06, "iqr": 7.30000024873334e-08, "q1": 5.058999999363323e-06, "q3": 5.132000001850656e-06, "iqr_outliers": 29499, "stddev_outliers": 3435, "outliers": "3435;29499", "ld15iqr": 4.94999999745005e-06, "hd15iqr": 5.242000000293956e-06, "ops": 176052.30815583322, "total": 0.6922772060001634, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-10000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-10000]", "params": { "fastmath": false, "parallel": false, "n": 10000 }, "param": "False-False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.5317000002568193e-05, "max": 0.0001715979999978856, "mean": 1.798729320889319e-05, "stddev": 7.312460171572777e-06, "rounds": 49712, "median": 1.6140999999691985e-05, "iqr": 1.1899999918796311e-07, "q1": 1.6093000002825875e-05, "q3": 1.6212000002013838e-05, "iqr_outliers": 9907, "stddev_outliers": 3368, "outliers": "3368;9907", "ld15iqr": 1.5931000000080076e-05, "hd15iqr": 1.6390999999060796e-05, "ops": 55594.80175180471, "total": 0.8941843200004982, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-31622]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-31622]", "params": { "fastmath": false, "parallel": false, "n": 31622 }, "param": "False-False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.250899999964531e-05, "max": 0.00023098600000537317, "mean": 5.8762171728307075e-05, "stddev": 1.6905532008580644e-05, "rounds": 16299, "median": 5.324499999659338e-05, "iqr": 5.850000022178392e-07, "q1": 5.287199999770564e-05, "q3": 5.345699999992348e-05, "iqr_outliers": 3407, "stddev_outliers": 1483, "outliers": "1483;3407", "ld15iqr": 5.250899999964531e-05, "hd15iqr": 5.4335000001515255e-05, "ops": 17017.750886124537, "total": 0.9577646369996771, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-100000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-100000]", "params": { "fastmath": false, "parallel": false, "n": 100000 }, "param": "False-False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0001557339999962437, "max": 0.0004167609999967681, "mean": 0.0001776748460486473, "stddev": 3.571705864297936e-05, "rounds": 4391, "median": 0.00016488499999667283, "iqr": 6.429500002624877e-06, "q1": 0.00016334399999529126, "q3": 0.00016977349999791613, "iqr_outliers": 818, "stddev_outliers": 500, "outliers": "500;818", "ld15iqr": 0.0001557339999962437, "hd15iqr": 0.0001797769999996035, "ops": 5628.258711006286, "total": 0.7801702489996103, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-316227]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-316227]", "params": { "fastmath": false, "parallel": false, "n": 316227 }, "param": "False-False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006041039999971076, "max": 0.001561467999998456, "mean": 0.000725377717692312, "stddev": 0.00016024841360708203, "rounds": 1300, "median": 0.0006513955000002625, "iqr": 0.00013348199999896337, "q1": 0.0006258485000003589, "q3": 0.0007593304999993222, "iqr_outliers": 128, "stddev_outliers": 172, "outliers": "172;128", "ld15iqr": 0.0006041039999971076, "hd15iqr": 0.0009613699999988512, "ops": 1378.5921122327284, "total": 0.9429910330000055, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-False-1000000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-False-1000000]", "params": { "fastmath": false, "parallel": false, "n": 1000000 }, "param": "False-False-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004517882000001805, "max": 0.006456591999999262, "mean": 0.005539401096491291, "stddev": 0.0003119458719998638, "rounds": 228, "median": 0.005589803999999532, "iqr": 0.0003723320000048602, "q1": 0.005370785499998476, "q3": 0.005743117500003336, "iqr_outliers": 6, "stddev_outliers": 59, "outliers": "59;6", "ld15iqr": 0.004819979000004082, "hd15iqr": 0.006456591999999262, "ops": 180.52493086904457, "total": 1.2629834500000143, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-10]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-10]", "params": { "fastmath": false, "parallel": true, "n": 10 }, "param": "False-True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.2930000014298457e-06, "max": 0.00013977399999731688, "mean": 2.9654290337888466e-06, "stddev": 3.2213711052195733e-06, "rounds": 144907, "median": 2.494000000297092e-06, "iqr": 1.3000000365082087e-07, "q1": 2.4289999984716815e-06, "q3": 2.5590000021225023e-06, "iqr_outliers": 19688, "stddev_outliers": 2877, "outliers": "2877;19688", "ld15iqr": 2.2930000014298457e-06, "hd15iqr": 2.754999997023333e-06, "ops": 337219.3327190594, "total": 0.4297114249992404, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-31]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-31]", "params": { "fastmath": false, "parallel": true, "n": 31 }, "param": "False-True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.2199999989425123e-06, "max": 0.00012292000000257985, "mean": 2.6337453622649823e-06, "stddev": 1.564083121951044e-06, "rounds": 185220, "median": 2.53599999666676e-06, "iqr": 2.0050000060223283e-07, "q1": 2.428500000206668e-06, "q3": 2.629000000808901e-06, "iqr_outliers": 1983, "stddev_outliers": 1334, "outliers": "1334;1983", "ld15iqr": 2.2199999989425123e-06, "hd15iqr": 2.9299999937393295e-06, "ops": 379687.42701079295, "total": 0.48782231599872006, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-100]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-100]", "params": { "fastmath": false, "parallel": true, "n": 100 }, "param": "False-True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.303999998787276e-06, "max": 0.00012516999999689915, "mean": 2.8794110737327086e-06, "stddev": 2.6350071267249425e-06, "rounds": 173672, "median": 2.5570000019570216e-06, "iqr": 1.4500000133921276e-07, "q1": 2.506999997820003e-06, "q3": 2.651999999159216e-06, "iqr_outliers": 9865, "stddev_outliers": 2496, "outliers": "2496;9865", "ld15iqr": 2.303999998787276e-06, "hd15iqr": 2.8699999958803346e-06, "ops": 347293.23962196737, "total": 0.500073079997307, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-316]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-316]", "params": { "fastmath": false, "parallel": true, "n": 316 }, "param": "False-True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.651000002629189e-06, "max": 0.0007230800000002091, "mean": 3.612061695701183e-06, "stddev": 4.688857260435486e-06, "rounds": 156348, "median": 2.889999997535142e-06, "iqr": 1.5100000183565498e-07, "q1": 2.8560000018273968e-06, "q3": 3.0070000036630518e-06, "iqr_outliers": 16901, "stddev_outliers": 4247, "outliers": "4247;16901", "ld15iqr": 2.651000002629189e-06, "hd15iqr": 3.2339999975761202e-06, "ops": 276850.1992062119, "total": 0.5647386219994885, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-1000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-1000]", "params": { "fastmath": false, "parallel": true, "n": 1000 }, "param": "False-True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 3.2270000005496513e-06, "max": 0.00014222000000074786, "mean": 3.86599585170078e-06, "stddev": 2.652394058101699e-06, "rounds": 139334, "median": 3.5559999957968103e-06, "iqr": 2.2399999721756103e-07, "q1": 3.4370000037142745e-06, "q3": 3.6610000009318355e-06, "iqr_outliers": 4063, "stddev_outliers": 2235, "outliers": "2235;4063", "ld15iqr": 3.2270000005496513e-06, "hd15iqr": 3.997000000310891e-06, "ops": 258665.56467205385, "total": 0.5386646660008765, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-3162]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-3162]", "params": { "fastmath": false, "parallel": true, "n": 3162 }, "param": "False-True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.1769999984685455e-06, "max": 0.00012922499999490356, "mean": 6.23081878861576e-06, "stddev": 3.2334501468683276e-06, "rounds": 112664, "median": 5.766000001017346e-06, "iqr": 2.8999999557299816e-07, "q1": 5.681000004642556e-06, "q3": 5.971000000215554e-06, "iqr_outliers": 5431, "stddev_outliers": 2834, "outliers": "2834;5431", "ld15iqr": 5.246999997154944e-06, "hd15iqr": 6.405999997127765e-06, "ops": 160492.55064632688, "total": 0.701988968000606, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-10000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-10000]", "params": { "fastmath": false, "parallel": true, "n": 10000 }, "param": "False-True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.1323000002505523e-05, "max": 0.0002611179999973956, "mean": 1.3936605927854537e-05, "stddev": 6.55507154859e-06, "rounds": 61641, "median": 1.2569999995548642e-05, "iqr": 6.752500070206224e-07, "q1": 1.2448999996195198e-05, "q3": 1.312425000321582e-05, "iqr_outliers": 4883, "stddev_outliers": 3371, "outliers": "3371;4883", "ld15iqr": 1.1436999997727071e-05, "hd15iqr": 1.4137999997387851e-05, "ops": 71753.48181448827, "total": 0.8590663259988816, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-31622]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-31622]", "params": { "fastmath": false, "parallel": true, "n": 31622 }, "param": "False-True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 3.0582000000833887e-05, "max": 0.00016446400000091899, "mean": 3.4908025296270915e-05, "stddev": 9.43187804471102e-06, "rounds": 25063, "median": 3.224499999987529e-05, "iqr": 1.5230000016686063e-06, "q1": 3.2120000000190885e-05, "q3": 3.364300000185949e-05, "iqr_outliers": 2143, "stddev_outliers": 1659, "outliers": "1659;2143", "ld15iqr": 3.0582000000833887e-05, "hd15iqr": 3.5932000002958375e-05, "ops": 28646.707784608658, "total": 0.874899838000438, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-100000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-100000]", "params": { "fastmath": false, "parallel": true, "n": 100000 }, "param": "False-True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.072600000337161e-05, "max": 0.000300341000006199, "mean": 0.00010426488576224787, "stddev": 2.267235254179405e-05, "rounds": 7143, "median": 9.734900000069047e-05, "iqr": 6.862499994042537e-06, "q1": 9.504225000256383e-05, "q3": 0.00010190474999660637, "iqr_outliers": 833, "stddev_outliers": 595, "outliers": "595;833", "ld15iqr": 9.072600000337161e-05, "hd15iqr": 0.00011222599999882732, "ops": 9590.956655151096, "total": 0.7447640789997365, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-316227]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-316227]", "params": { "fastmath": false, "parallel": true, "n": 316227 }, "param": "False-True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0002935079999986101, "max": 0.001218880999999783, "mean": 0.0003769477158383957, "stddev": 0.00010447618990144026, "rounds": 1932, "median": 0.00032598900000024855, "iqr": 0.00011434200000337569, "q1": 0.00030775849999642446, "q3": 0.00042210049999980015, "iqr_outliers": 111, "stddev_outliers": 270, "outliers": "270;111", "ld15iqr": 0.0002935079999986101, "hd15iqr": 0.0005952469999996879, "ops": 2652.887809058161, "total": 0.7282629869997805, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[False-True-1000000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[False-True-1000000]", "params": { "fastmath": false, "parallel": true, "n": 1000000 }, "param": "False-True-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0010692270000021153, "max": 0.0016584909999934894, "mean": 0.0014053659056599997, "stddev": 0.00010938705043474735, "rounds": 742, "median": 0.0014287605000014025, "iqr": 0.0001535330000095314, "q1": 0.0013364219999942861, "q3": 0.0014899550000038175, "iqr_outliers": 1, "stddev_outliers": 235, "outliers": "235;1", "ld15iqr": 0.0011212009999965744, "hd15iqr": 0.0016584909999934894, "ops": 711.5584603074398, "total": 1.0427815019997198, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-10]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-10]", "params": { "fastmath": true, "parallel": false, "n": 10 }, "param": "True-False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 4.2525000019111305e-07, "max": 6.327250000026652e-06, "mean": 5.276509057261997e-07, "stddev": 2.5985263902639337e-07, "rounds": 107262, "median": 4.5859999993069777e-07, "iqr": 2.835000003642566e-08, "q1": 4.5375000006231404e-07, "q3": 4.821000000987397e-07, "iqr_outliers": 10751, "stddev_outliers": 6206, "outliers": "6206;10751", "ld15iqr": 4.2525000019111305e-07, "hd15iqr": 5.246500002442644e-07, "ops": 1895192.4258023659, "total": 0.05659689145000069, "iterations": 20 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-31]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-31]", "params": { "fastmath": true, "parallel": false, "n": 31 }, "param": "True-False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.998500000714558e-07, "max": 8.326300000049968e-06, "mean": 7.364908693010957e-07, "stddev": 3.1494067713892395e-07, "rounds": 70597, "median": 6.688500000961994e-07, "iqr": 4.175000007933254e-08, "q1": 6.566999999790823e-07, "q3": 6.984500000584148e-07, "iqr_outliers": 4606, "stddev_outliers": 3911, "outliers": "3911;4606", "ld15iqr": 5.998500000714558e-07, "hd15iqr": 7.611999997436669e-07, "ops": 1357790.0849591773, "total": 0.051994045900049815, "iterations": 20 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-100]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-100]", "params": { "fastmath": true, "parallel": false, "n": 100 }, "param": "True-False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 6.781499997998708e-07, "max": 6.672919999992644e-05, "mean": 8.573905086876151e-07, "stddev": 4.852130215400721e-07, "rounds": 66819, "median": 7.606000000492941e-07, "iqr": 4.069999981481942e-08, "q1": 7.39200000055007e-07, "q3": 7.798999998698264e-07, "iqr_outliers": 6747, "stddev_outliers": 4465, "outliers": "4465;6747", "ld15iqr": 6.792500002461566e-07, "hd15iqr": 8.410500001332366e-07, "ops": 1166329.6827610508, "total": 0.05728997639999992, "iterations": 20 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-316]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-316]", "params": { "fastmath": true, "parallel": false, "n": 316 }, "param": "True-False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 8.33833333047096e-07, "max": 0.0006090733333330434, "mean": 1.1376584217867735e-06, "stddev": 1.7193758544463329e-06, "rounds": 174856, "median": 9.371666666406023e-07, "iqr": 5.416666700360417e-08, "q1": 9.09666666141599e-07, "q3": 9.638333331452031e-07, "iqr_outliers": 29951, "stddev_outliers": 7628, "outliers": "7628;29951", "ld15iqr": 8.33833333047096e-07, "hd15iqr": 1.0451666661026593e-06, "ops": 878998.4593349568, "total": 0.1989264009999456, "iterations": 6 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-1000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-1000]", "params": { "fastmath": true, "parallel": false, "n": 1000 }, "param": "True-False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.9029999975828105e-06, "max": 0.0001145579999928259, "mean": 2.5136941765871796e-06, "stddev": 2.748865927021821e-06, "rounds": 190404, "median": 2.117999997608422e-06, "iqr": 9.200000761211413e-08, "q1": 2.078999997934261e-06, "q3": 2.171000005546375e-06, "iqr_outliers": 31316, "stddev_outliers": 3712, "outliers": "3712;31316", "ld15iqr": 1.940999993621517e-06, "hd15iqr": 2.3099999992837184e-06, "ops": 397820.8683117097, "total": 0.47861742599890533, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-3162]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-3162]", "params": { "fastmath": true, "parallel": false, "n": 3162 }, "param": "True-False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 4.888999995955601e-06, "max": 0.0016173559999970166, "mean": 6.67323438642645e-06, "stddev": 7.125385296796051e-06, "rounds": 117814, "median": 5.28900000063004e-06, "iqr": 7.889999977805928e-07, "q1": 5.217999998308187e-06, "q3": 6.00699999608878e-06, "iqr_outliers": 18184, "stddev_outliers": 5567, "outliers": "5567;18184", "ld15iqr": 4.888999995955601e-06, "hd15iqr": 7.190999994577396e-06, "ops": 149852.3717425584, "total": 0.7862004360024457, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-10000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-10000]", "params": { "fastmath": true, "parallel": false, "n": 10000 }, "param": "True-False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 1.590200000123332e-05, "max": 0.00020341500000142787, "mean": 2.001629543258794e-05, "stddev": 9.9266641487268e-06, "rounds": 45540, "median": 1.691299999606599e-05, "iqr": 8.679999865535137e-07, "q1": 1.6245000011849697e-05, "q3": 1.711299999840321e-05, "iqr_outliers": 8686, "stddev_outliers": 5031, "outliers": "5031;8686", "ld15iqr": 1.590200000123332e-05, "hd15iqr": 1.8414999999549764e-05, "ops": 49959.294584148156, "total": 0.9115420940000547, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-31622]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-31622]", "params": { "fastmath": true, "parallel": false, "n": 31622 }, "param": "True-False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 5.195599999296974e-05, "max": 0.00026434200000835517, "mean": 6.409864415755342e-05, "stddev": 2.298564850533202e-05, "rounds": 6042, "median": 5.50729999986288e-05, "iqr": 2.561000002287983e-06, "q1": 5.28730000013411e-05, "q3": 5.543400000362908e-05, "iqr_outliers": 1359, "stddev_outliers": 798, "outliers": "798;1359", "ld15iqr": 5.195599999296974e-05, "hd15iqr": 5.9315999990872115e-05, "ops": 15600.954016156977, "total": 0.3872840079999378, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-100000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-100000]", "params": { "fastmath": true, "parallel": false, "n": 100000 }, "param": "True-False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00015974299999754749, "max": 0.0005250009999997474, "mean": 0.00019991409436661905, "stddev": 5.6284982081303065e-05, "rounds": 3497, "median": 0.00017131999999264735, "iqr": 4.23897500105852e-05, "q1": 0.0001695609999927683, "q3": 0.0002119507500033535, "iqr_outliers": 396, "stddev_outliers": 515, "outliers": "515;396", "ld15iqr": 0.00015974299999754749, "hd15iqr": 0.0002756389999944986, "ops": 5002.148563703153, "total": 0.6990995880000668, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-316227]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-316227]", "params": { "fastmath": true, "parallel": false, "n": 316227 }, "param": "True-False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0005987210000029108, "max": 0.001858225999995966, "mean": 0.000793979701890818, "stddev": 0.0001924912937560062, "rounds": 899, "median": 0.0007148859999972501, "iqr": 0.00027998899999914784, "q1": 0.0006326717500009238, "q3": 0.0009126607500000716, "iqr_outliers": 10, "stddev_outliers": 155, "outliers": "155;10", "ld15iqr": 0.0005987210000029108, "hd15iqr": 0.0013555880000097886, "ops": 1259.4780415904288, "total": 0.7137877519998455, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-False-1000000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-False-1000000]", "params": { "fastmath": true, "parallel": false, "n": 1000000 }, "param": "True-False-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004206049000003986, "max": 0.006451630000000819, "mean": 0.005544679136364101, "stddev": 0.0003724681762288862, "rounds": 220, "median": 0.005616597000006607, "iqr": 0.00047781199999974433, "q1": 0.005313022999999362, "q3": 0.005790834999999106, "iqr_outliers": 4, "stddev_outliers": 58, "outliers": "58;4", "ld15iqr": 0.004679873000000612, "hd15iqr": 0.006451630000000819, "ops": 180.35308724027368, "total": 1.2198294100001021, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-10]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-10]", "params": { "fastmath": true, "parallel": true, "n": 10 }, "param": "True-True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.3770000012746095e-06, "max": 8.785100000352486e-05, "mean": 2.869558150655784e-06, "stddev": 2.5634970718002163e-06, "rounds": 43843, "median": 2.5719999996454135e-06, "iqr": 6.899998084008985e-08, "q1": 2.5320000105466534e-06, "q3": 2.600999991386743e-06, "iqr_outliers": 4040, "stddev_outliers": 700, "outliers": "700;4040", "ld15iqr": 2.428999991366254e-06, "hd15iqr": 2.7049999999917418e-06, "ops": 348485.70668326365, "total": 0.12581003799920154, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-31]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-31]", "params": { "fastmath": true, "parallel": true, "n": 31 }, "param": "True-True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.389000002267494e-06, "max": 0.0002836330000093312, "mean": 2.96322942001911e-06, "stddev": 2.5824245072081864e-06, "rounds": 164881, "median": 2.6139999960150817e-06, "iqr": 1.2000000992884452e-07, "q1": 2.566999995678998e-06, "q3": 2.6870000056078425e-06, "iqr_outliers": 13447, "stddev_outliers": 2621, "outliers": "2621;13447", "ld15iqr": 2.389000002267494e-06, "hd15iqr": 2.867999995714854e-06, "ops": 337469.65160515683, "total": 0.4885802300021709, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-100]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-100]", "params": { "fastmath": true, "parallel": true, "n": 100 }, "param": "True-True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.4869999890597683e-06, "max": 0.00017010599999878195, "mean": 3.2954506166353633e-06, "stddev": 3.4040333843371385e-06, "rounds": 170766, "median": 2.7100000039581573e-06, "iqr": 1.610000168739134e-07, "q1": 2.6609999963511655e-06, "q3": 2.822000013225079e-06, "iqr_outliers": 19746, "stddev_outliers": 4008, "outliers": "4008;19746", "ld15iqr": 2.4869999890597683e-06, "hd15iqr": 3.063999997721112e-06, "ops": 303448.63763153413, "total": 0.5627509200003544, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-316]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-316]", "params": { "fastmath": true, "parallel": true, "n": 316 }, "param": "True-True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.4320000022726163e-06, "max": 0.000343454999992332, "mean": 3.290745510603073e-06, "stddev": 3.3747928953426736e-06, "rounds": 161213, "median": 2.7200000118909884e-06, "iqr": 1.4900000167017424e-07, "q1": 2.663999993046673e-06, "q3": 2.812999994716847e-06, "iqr_outliers": 19819, "stddev_outliers": 3690, "outliers": "3690;19819", "ld15iqr": 2.4419999959945926e-06, "hd15iqr": 3.0369999990398355e-06, "ops": 303882.50831852894, "total": 0.5305109560008532, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-1000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-1000]", "params": { "fastmath": true, "parallel": true, "n": 1000 }, "param": "True-True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.6120000029550283e-06, "max": 0.00016679300000532749, "mean": 3.390966656527658e-06, "stddev": 3.3982203598919475e-06, "rounds": 151424, "median": 2.901000001998e-06, "iqr": 1.5599999869664316e-07, "q1": 2.8150000019877552e-06, "q3": 2.9710000006843984e-06, "iqr_outliers": 17510, "stddev_outliers": 3331, "outliers": "3331;17510", "ld15iqr": 2.6120000029550283e-06, "hd15iqr": 3.205000012940218e-06, "ops": 294901.1598433108, "total": 0.513473734998044, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-3162]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-3162]", "params": { "fastmath": true, "parallel": true, "n": 3162 }, "param": "True-True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 3.030999991437966e-06, "max": 0.0018878150000034566, "mean": 4.191692589570426e-06, "stddev": 7.0054428267911025e-06, "rounds": 150626, "median": 3.3860000030472293e-06, "iqr": 1.4500000133921276e-07, "q1": 3.3240000050227536e-06, "q3": 3.4690000063619664e-06, "iqr_outliers": 26754, "stddev_outliers": 4427, "outliers": "4427;26754", "ld15iqr": 3.106999997726234e-06, "hd15iqr": 3.6869999888722305e-06, "ops": 238567.11307698314, "total": 0.631377887996635, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-10000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-10000]", "params": { "fastmath": true, "parallel": true, "n": 10000 }, "param": "True-True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 4.261999990262666e-06, "max": 0.00016315700000291145, "mean": 5.513823705609758e-06, "stddev": 4.4255046361270606e-06, "rounds": 128189, "median": 4.741000012131735e-06, "iqr": 2.499999993688107e-07, "q1": 4.5860000028596914e-06, "q3": 4.836000002228502e-06, "iqr_outliers": 13608, "stddev_outliers": 4658, "outliers": "4658;13608", "ld15iqr": 4.261999990262666e-06, "hd15iqr": 5.2119999907063175e-06, "ops": 181362.34551398535, "total": 0.7068115469984093, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-31622]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-31622]", "params": { "fastmath": true, "parallel": true, "n": 31622 }, "param": "True-True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 9.149999996793667e-06, "max": 0.00016548899999691002, "mean": 1.1977842146838918e-05, "stddev": 7.249386427116504e-06, "rounds": 31675, "median": 9.897999987629191e-06, "iqr": 3.599999871539694e-07, "q1": 9.802000008107825e-06, "q3": 1.0161999995261795e-05, "iqr_outliers": 5640, "stddev_outliers": 2571, "outliers": "2571;5640", "ld15iqr": 9.262999995485188e-06, "hd15iqr": 1.0703999990369084e-05, "ops": 83487.49196564681, "total": 0.37939815000112276, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-100000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-100000]", "params": { "fastmath": true, "parallel": true, "n": 100000 }, "param": "True-True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 2.2526999998717656e-05, "max": 0.00038498199999992266, "mean": 3.00487217428664e-05, "stddev": 1.4126930086363842e-05, "rounds": 25383, "median": 2.4766999999314976e-05, "iqr": 1.6147499977137159e-06, "q1": 2.4354999997200366e-05, "q3": 2.5969749994914082e-05, "iqr_outliers": 5157, "stddev_outliers": 2812, "outliers": "2812;5157", "ld15iqr": 2.2526999998717656e-05, "hd15iqr": 2.839399999743364e-05, "ops": 33279.285839751275, "total": 0.7627267039991779, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-316227]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-316227]", "params": { "fastmath": true, "parallel": true, "n": 316227 }, "param": "True-True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 7.126100000220958e-05, "max": 0.0013527760000044964, "mean": 0.00010187946724624525, "stddev": 4.637387629720374e-05, "rounds": 6442, "median": 7.959999999940237e-05, "iqr": 3.755599999522019e-05, "q1": 7.624799999916831e-05, "q3": 0.0001138039999943885, "iqr_outliers": 571, "stddev_outliers": 925, "outliers": "925;571", "ld15iqr": 7.126100000220958e-05, "hd15iqr": 0.0001703669999955082, "ops": 9815.520507022035, "total": 0.6563075280003119, "iterations": 1 } }, { "group": null, "name": "test_numba_sum_logpdf[True-True-1000000]", "fullname": "bench/test_cost.py::test_numba_sum_logpdf[True-True-1000000]", "params": { "fastmath": true, "parallel": true, "n": 1000000 }, "param": "True-True-1000000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00032303599999750077, "max": 0.0008806449999951838, "mean": 0.00041048534172643224, "stddev": 7.492473906070616e-05, "rounds": 1668, "median": 0.0003826869999912219, "iqr": 7.027049999663859e-05, "q1": 0.0003624620000053369, "q3": 0.0004327325000019755, "iqr_outliers": 128, "stddev_outliers": 271, "outliers": "271;128", "ld15iqr": 0.00032303599999750077, "hd15iqr": 0.0005382989999986876, "ops": 2436.1405837152874, "total": 0.684689549999689, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-10]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-10]", "params": { "numba": false, "log": false, "n": 10 }, "param": "False-False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0017344800000103078, "max": 0.0041282650000056265, "mean": 0.0021414910851063726, "stddev": 0.00034513590580946964, "rounds": 376, "median": 0.0020513134999973204, "iqr": 0.0003378595000000928, "q1": 0.00189626299999901, "q3": 0.0022341224999991027, "iqr_outliers": 30, "stddev_outliers": 66, "outliers": "66;30", "ld15iqr": 0.0017344800000103078, "hd15iqr": 0.002772308999993811, "ops": 466.9643534613771, "total": 0.805200647999996, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-31]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-31]", "params": { "numba": false, "log": false, "n": 31 }, "param": "False-False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008828080000000682, "max": 0.0020847910000014735, "mean": 0.0010923882206673027, "stddev": 0.00020940821009605564, "rounds": 929, "median": 0.0010156419999987065, "iqr": 0.00022649024999665812, "q1": 0.0009411497500018129, "q3": 0.001167639999998471, "iqr_outliers": 56, "stddev_outliers": 129, "outliers": "129;56", "ld15iqr": 0.0008828080000000682, "hd15iqr": 0.00150755599999286, "ops": 915.4254697008121, "total": 1.0148286569999243, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-100]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-100]", "params": { "numba": false, "log": false, "n": 100 }, "param": "False-False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009083310000050915, "max": 0.0019698910000016667, "mean": 0.0010831048155651986, "stddev": 0.00019561254309250845, "rounds": 938, "median": 0.0009991424999995502, "iqr": 0.00022228399998880377, "q1": 0.0009399099999995997, "q3": 0.0011621939999884034, "iqr_outliers": 47, "stddev_outliers": 131, "outliers": "131;47", "ld15iqr": 0.0009083310000050915, "hd15iqr": 0.001495941999991146, "ops": 923.2716775228888, "total": 1.0159523170001563, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-316]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-316]", "params": { "numba": false, "log": false, "n": 316 }, "param": "False-False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0011275840000024573, "max": 0.0026859690000122782, "mean": 0.0013931383106435605, "stddev": 0.0002711524370633534, "rounds": 808, "median": 0.0013117589999893653, "iqr": 0.00027960450000108494, "q1": 0.0011993314999969584, "q3": 0.0014789359999980434, "iqr_outliers": 55, "stddev_outliers": 115, "outliers": "115;55", "ld15iqr": 0.0011275840000024573, "hd15iqr": 0.0019012909999958083, "ops": 717.8038191613939, "total": 1.1256557549999968, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-1000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-1000]", "params": { "numba": false, "log": false, "n": 1000 }, "param": "False-False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.001243679000012321, "max": 0.003187733999993725, "mean": 0.0015580237327586924, "stddev": 0.00029879129198301976, "rounds": 696, "median": 0.0014769430000001194, "iqr": 0.00033460450000433184, "q1": 0.0013272039999989715, "q3": 0.0016618085000033034, "iqr_outliers": 40, "stddev_outliers": 123, "outliers": "123;40", "ld15iqr": 0.001243679000012321, "hd15iqr": 0.002164116999992416, "ops": 641.8387467239438, "total": 1.08438451800005, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-3162]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-3162]", "params": { "numba": false, "log": false, "n": 3162 }, "param": "False-False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0014341560000019626, "max": 0.0033118289999976014, "mean": 0.0018275347154604706, "stddev": 0.0003623601156891203, "rounds": 608, "median": 0.0017206160000000637, "iqr": 0.0003861669999949413, "q1": 0.001543353000002412, "q3": 0.0019295199999973534, "iqr_outliers": 48, "stddev_outliers": 100, "outliers": "100;48", "ld15iqr": 0.0014341560000019626, "hd15iqr": 0.0025115819999967925, "ops": 547.1852280234453, "total": 1.1111411069999662, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-10000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-10000]", "params": { "numba": false, "log": false, "n": 10000 }, "param": "False-False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0032603289999997287, "max": 0.00596613300000115, "mean": 0.0040838711266375175, "stddev": 0.0005379164641770408, "rounds": 229, "median": 0.00399909300000445, "iqr": 0.0007170995000080893, "q1": 0.0036451789999922823, "q3": 0.004362278500000372, "iqr_outliers": 3, "stddev_outliers": 70, "outliers": "70;3", "ld15iqr": 0.0032603289999997287, "hd15iqr": 0.005591271999989544, "ops": 244.86570927211326, "total": 0.9352064879999915, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-31622]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-31622]", "params": { "numba": false, "log": false, "n": 31622 }, "param": "False-False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.012078066000000831, "max": 0.018781547999992654, "mean": 0.013415765839285245, "stddev": 0.001054360041583573, "rounds": 56, "median": 0.013215752000000691, "iqr": 0.0010431765000049609, "q1": 0.012766638999991642, "q3": 0.013809815499996603, "iqr_outliers": 2, "stddev_outliers": 8, "outliers": "8;2", "ld15iqr": 0.012078066000000831, "hd15iqr": 0.016091967000008367, "ops": 74.53916623020585, "total": 0.7512828869999737, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-100000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-100000]", "params": { "numba": false, "log": false, "n": 100000 }, "param": "False-False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.028532187999999792, "max": 0.03257806400000618, "mean": 0.030168462875001634, "stddev": 0.0010069044608002923, "rounds": 32, "median": 0.029816428499998437, "iqr": 0.0012382594999920116, "q1": 0.029552801000008344, "q3": 0.030791060500000356, "iqr_outliers": 0, "stddev_outliers": 9, "outliers": "9;0", "ld15iqr": 0.028532187999999792, "hd15iqr": 0.03257806400000618, "ops": 33.14719759317356, "total": 0.9653908120000523, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-False-316227]", "fullname": "bench/test_cost.py::test_minuit_custom[False-False-316227]", "params": { "numba": false, "log": false, "n": 316227 }, "param": "False-False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.1348924870000019, "max": 0.14091586800000755, "mean": 0.13745938849999995, "stddev": 0.002217329713866885, "rounds": 8, "median": 0.13775680399999857, "iqr": 0.0037236874999990732, "q1": 0.1352264424999987, "q3": 0.13895012999999778, "iqr_outliers": 0, "stddev_outliers": 3, "outliers": "3;0", "ld15iqr": 0.1348924870000019, "hd15iqr": 0.14091586800000755, "ops": 7.274875953634846, "total": 1.0996751079999996, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-10]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-10]", "params": { "numba": false, "log": true, "n": 10 }, "param": "False-True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.001266528000002154, "max": 0.002670139999992216, "mean": 0.001511692147388548, "stddev": 0.00029771554978151555, "rounds": 536, "median": 0.001384672500002182, "iqr": 0.00022811350000750963, "q1": 0.0013347789999968995, "q3": 0.0015628925000044092, "iqr_outliers": 62, "stddev_outliers": 70, "outliers": "70;62", "ld15iqr": 0.001266528000002154, "hd15iqr": 0.0019159369999925957, "ops": 661.5103490002924, "total": 0.8102669910002618, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-31]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-31]", "params": { "numba": false, "log": true, "n": 31 }, "param": "False-True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008195010000093816, "max": 0.00237106100000517, "mean": 0.0010072394748206137, "stddev": 0.00023668967424665633, "rounds": 695, "median": 0.0008981460000114794, "iqr": 0.0001717067499953373, "q1": 0.0008661062500046057, "q3": 0.001037812999999943, "iqr_outliers": 85, "stddev_outliers": 97, "outliers": "97;85", "ld15iqr": 0.0008195010000093816, "hd15iqr": 0.001296014000004675, "ops": 992.8125584812857, "total": 0.7000314350003265, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-100]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-100]", "params": { "numba": false, "log": true, "n": 100 }, "param": "False-True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007900579999926549, "max": 0.002130019999995625, "mean": 0.0009970345534532489, "stddev": 0.00022869089032350124, "rounds": 1057, "median": 0.0008880030000000261, "iqr": 0.00017210600000083787, "q1": 0.000867814250000265, "q3": 0.0010399202500011029, "iqr_outliers": 108, "stddev_outliers": 133, "outliers": "133;108", "ld15iqr": 0.0007900579999926549, "hd15iqr": 0.0013007349999867301, "ops": 1002.9742665752958, "total": 1.0538655230000842, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-316]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-316]", "params": { "numba": false, "log": true, "n": 316 }, "param": "False-True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009590579999922966, "max": 0.002438382999997657, "mean": 0.0011908449200866893, "stddev": 0.0002753196635742493, "rounds": 926, "median": 0.0010627799999980425, "iqr": 0.00026227499999720294, "q1": 0.001015735000009954, "q3": 0.001278010000007157, "iqr_outliers": 73, "stddev_outliers": 129, "outliers": "129;73", "ld15iqr": 0.0009590579999922966, "hd15iqr": 0.0016742699999952038, "ops": 839.73990494682, "total": 1.1027223960002743, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-1000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-1000]", "params": { "numba": false, "log": true, "n": 1000 }, "param": "False-True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008996469999971168, "max": 0.002533948000007058, "mean": 0.001122879915151448, "stddev": 0.0002661797058956045, "rounds": 495, "median": 0.0009918009999978494, "iqr": 0.00022886399999322293, "q1": 0.0009561847500023646, "q3": 0.0011850487499955875, "iqr_outliers": 47, "stddev_outliers": 72, "outliers": "72;47", "ld15iqr": 0.0008996469999971168, "hd15iqr": 0.001557597000001465, "ops": 890.5671804318679, "total": 0.5558255579999667, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-3162]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-3162]", "params": { "numba": false, "log": true, "n": 3162 }, "param": "False-True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007653779999969856, "max": 0.002001369999987901, "mean": 0.0009433106150064317, "stddev": 0.00022681224343272042, "rounds": 813, "median": 0.0008336240000090811, "iqr": 0.0001813799999972332, "q1": 0.0008093120000012277, "q3": 0.000990691999998461, "iqr_outliers": 86, "stddev_outliers": 118, "outliers": "118;86", "ld15iqr": 0.0007653779999969856, "hd15iqr": 0.001264914000003614, "ops": 1060.096201708895, "total": 0.7669115300002289, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-10000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-10000]", "params": { "numba": false, "log": true, "n": 10000 }, "param": "False-True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0011349509999973861, "max": 0.002723068000008766, "mean": 0.0014135167107025514, "stddev": 0.00029743002774712714, "rounds": 598, "median": 0.0012958754999985445, "iqr": 0.0002950410000011061, "q1": 0.001206702999994036, "q3": 0.001501743999995142, "iqr_outliers": 46, "stddev_outliers": 86, "outliers": "86;46", "ld15iqr": 0.0011349509999973861, "hd15iqr": 0.0019505309999914289, "ops": 707.4553787927814, "total": 0.8452829930001258, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-31622]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-31622]", "params": { "numba": false, "log": true, "n": 31622 }, "param": "False-True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0024581879999914236, "max": 0.004871679000004292, "mean": 0.003061960565350218, "stddev": 0.00046932590081302344, "rounds": 329, "median": 0.0028868889999955627, "iqr": 0.0006882747500007724, "q1": 0.002695703249997905, "q3": 0.0033839779999986774, "iqr_outliers": 2, "stddev_outliers": 82, "outliers": "82;2", "ld15iqr": 0.0024581879999914236, "hd15iqr": 0.004499139999992963, "ops": 326.5881381087032, "total": 1.0073850260002217, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-100000]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-100000]", "params": { "numba": false, "log": true, "n": 100000 }, "param": "False-True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0048042859999952725, "max": 0.008324477999991586, "mean": 0.005830165062893427, "stddev": 0.000842987564016931, "rounds": 159, "median": 0.0056534279999880255, "iqr": 0.0014428974999987076, "q1": 0.005083976250002564, "q3": 0.006526873750001272, "iqr_outliers": 0, "stddev_outliers": 57, "outliers": "57;0", "ld15iqr": 0.0048042859999952725, "hd15iqr": 0.008324477999991586, "ops": 171.52173038197213, "total": 0.9269962450000548, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[False-True-316227]", "fullname": "bench/test_cost.py::test_minuit_custom[False-True-316227]", "params": { "numba": false, "log": true, "n": 316227 }, "param": "False-True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.022271719000002577, "max": 0.0275136759999981, "mean": 0.024295345702703235, "stddev": 0.0013508909801599129, "rounds": 37, "median": 0.023977650999995603, "iqr": 0.0018118857500049046, "q1": 0.023361240249997195, "q3": 0.0251731260000021, "iqr_outliers": 0, "stddev_outliers": 9, "outliers": "9;0", "ld15iqr": 0.022271719000002577, "hd15iqr": 0.0275136759999981, "ops": 41.160146977811245, "total": 0.8989277910000197, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-10]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-10]", "params": { "numba": true, "log": false, "n": 10 }, "param": "True-False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006247840000099814, "max": 0.0016997040000035213, "mean": 0.0007364066695156964, "stddev": 0.00013824279332687748, "rounds": 1053, "median": 0.0006690249999934395, "iqr": 0.0001570274999949106, "q1": 0.0006363307500016901, "q3": 0.0007933582499966008, "iqr_outliers": 50, "stddev_outliers": 136, "outliers": "136;50", "ld15iqr": 0.0006247840000099814, "hd15iqr": 0.0010297870000073317, "ops": 1357.9453329199991, "total": 0.7754362230000282, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-31]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-31]", "params": { "numba": true, "log": false, "n": 31 }, "param": "True-False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004155829999916705, "max": 0.0014971849999909637, "mean": 0.0005144389983598676, "stddev": 0.00012006889660948242, "rounds": 1829, "median": 0.00045067599999981667, "iqr": 0.00011892400000590442, "q1": 0.0004417722499958643, "q3": 0.0005606962500017687, "iqr_outliers": 109, "stddev_outliers": 270, "outliers": "270;109", "ld15iqr": 0.0004155829999916705, "hd15iqr": 0.0007393000000064376, "ops": 1943.8650708600942, "total": 0.9409089280001979, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-100]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-100]", "params": { "numba": true, "log": false, "n": 100 }, "param": "True-False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004419530000063787, "max": 0.0011699809999896615, "mean": 0.000541588804844375, "stddev": 0.00012835751210447753, "rounds": 1445, "median": 0.00047369000000685446, "iqr": 0.0001397294999954113, "q1": 0.00045465125000276885, "q3": 0.0005943807499981801, "iqr_outliers": 78, "stddev_outliers": 229, "outliers": "229;78", "ld15iqr": 0.0004419530000063787, "hd15iqr": 0.0008043440000022883, "ops": 1846.4192595106335, "total": 0.782595823000122, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-316]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-316]", "params": { "numba": true, "log": false, "n": 316 }, "param": "True-False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0005664549999977453, "max": 0.0013976510000048847, "mean": 0.0006828348768269093, "stddev": 0.0001516300455717845, "rounds": 1437, "median": 0.0006034110000001647, "iqr": 0.0001802785000037943, "q1": 0.000572579999996492, "q3": 0.0007528585000002863, "iqr_outliers": 62, "stddev_outliers": 223, "outliers": "223;62", "ld15iqr": 0.0005664549999977453, "hd15iqr": 0.001024354000008998, "ops": 1464.4828990676883, "total": 0.9812337180002686, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-1000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-1000]", "params": { "numba": true, "log": false, "n": 1000 }, "param": "True-False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007363069999968275, "max": 0.0021546970000088095, "mean": 0.0009467173763704371, "stddev": 0.0002562169552203185, "rounds": 821, "median": 0.0008324180000016668, "iqr": 0.00023939500000125236, "q1": 0.0007829792500011479, "q3": 0.0010223742500024002, "iqr_outliers": 64, "stddev_outliers": 107, "outliers": "107;64", "ld15iqr": 0.0007363069999968275, "hd15iqr": 0.0013873550000056412, "ops": 1056.2814467754251, "total": 0.7772549660001289, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-3162]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-3162]", "params": { "numba": true, "log": false, "n": 3162 }, "param": "True-False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.001129839000000743, "max": 0.0024871709999985114, "mean": 0.0014112488782049412, "stddev": 0.00025772909993452935, "rounds": 468, "median": 0.0013277479999942443, "iqr": 0.00030073650000161933, "q1": 0.0012109054999953628, "q3": 0.0015116419999969821, "iqr_outliers": 27, "stddev_outliers": 88, "outliers": "88;27", "ld15iqr": 0.001129839000000743, "hd15iqr": 0.0019648090000004004, "ops": 708.5922373040004, "total": 0.6604644749999125, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-10000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-10000]", "params": { "numba": true, "log": false, "n": 10000 }, "param": "True-False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0028462549999943576, "max": 0.005148325999996928, "mean": 0.003433433589905115, "stddev": 0.0004043515901989944, "rounds": 317, "median": 0.0033321929999914346, "iqr": 0.0006182132500072157, "q1": 0.0031049022499907153, "q3": 0.003723115499997931, "iqr_outliers": 2, "stddev_outliers": 91, "outliers": "91;2", "ld15iqr": 0.0028462549999943576, "hd15iqr": 0.004794410000002358, "ops": 291.2536310415824, "total": 1.0883984479999214, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-31622]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-31622]", "params": { "numba": true, "log": false, "n": 31622 }, "param": "True-False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.01161169799999584, "max": 0.014282819999991148, "mean": 0.013063198207316943, "stddev": 0.0006028859771067187, "rounds": 82, "median": 0.013191876000000491, "iqr": 0.0005937240000122301, "q1": 0.012847469999996974, "q3": 0.013441194000009205, "iqr_outliers": 7, "stddev_outliers": 22, "outliers": "22;7", "ld15iqr": 0.011996899999999755, "hd15iqr": 0.014282819999991148, "ops": 76.55093217829928, "total": 1.0711822529999893, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-100000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-100000]", "params": { "numba": true, "log": false, "n": 100000 }, "param": "True-False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.03272018700000956, "max": 0.03617866599999786, "mean": 0.03391822135483881, "stddev": 0.0007924347345359764, "rounds": 31, "median": 0.03377913299999591, "iqr": 0.0007900052500069421, "q1": 0.03340289824999587, "q3": 0.034192903500002814, "iqr_outliers": 2, "stddev_outliers": 7, "outliers": "7;2", "ld15iqr": 0.03272018700000956, "hd15iqr": 0.03596168900000407, "ops": 29.48267804312029, "total": 1.051464862000003, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-False-316227]", "fullname": "bench/test_cost.py::test_minuit_custom[True-False-316227]", "params": { "numba": true, "log": false, "n": 316227 }, "param": "True-False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.11665477299999338, "max": 0.1410759789999929, "mean": 0.12520272274999478, "stddev": 0.00810219504828115, "rounds": 8, "median": 0.12357879749998801, "iqr": 0.010663498500001367, "q1": 0.11885160949999829, "q3": 0.12951510799999966, "iqr_outliers": 0, "stddev_outliers": 3, "outliers": "3;0", "ld15iqr": 0.11665477299999338, "hd15iqr": 0.1410759789999929, "ops": 7.987046751345843, "total": 1.0016217819999582, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-10]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-10]", "params": { "numba": true, "log": true, "n": 10 }, "param": "True-True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.000514675000005127, "max": 0.001299927000019352, "mean": 0.0006025002745481143, "stddev": 0.0001336977721421544, "rounds": 1548, "median": 0.000543470499991372, "iqr": 0.00010868399998287259, "q1": 0.0005201235000100723, "q3": 0.0006288074999929449, "iqr_outliers": 153, "stddev_outliers": 211, "outliers": "211;153", "ld15iqr": 0.000514675000005127, "hd15iqr": 0.0007927520000237109, "ops": 1659.7502810268716, "total": 0.9326704250004809, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-31]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-31]", "params": { "numba": true, "log": true, "n": 31 }, "param": "True-True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0003904719999923145, "max": 0.0009447790000081113, "mean": 0.00044771645565514363, "stddev": 9.136295565787653e-05, "rounds": 1556, "median": 0.0004084115000040356, "iqr": 6.77675000133604e-05, "q1": 0.0003944184999937761, "q3": 0.0004621860000071365, "iqr_outliers": 168, "stddev_outliers": 214, "outliers": "214;168", "ld15iqr": 0.0003904719999923145, "hd15iqr": 0.0005640329999891946, "ops": 2233.556500702436, "total": 0.6966468049994035, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-100]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-100]", "params": { "numba": true, "log": true, "n": 100 }, "param": "True-True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00039467700000273, "max": 0.0010728569999969295, "mean": 0.0004754076685305402, "stddev": 0.00010500805596300418, "rounds": 1967, "median": 0.0004204650000190213, "iqr": 0.00010018425001590003, "q1": 0.0004143244999923468, "q3": 0.0005145087500082468, "iqr_outliers": 145, "stddev_outliers": 320, "outliers": "320;145", "ld15iqr": 0.00039467700000273, "hd15iqr": 0.0006664209999769355, "ops": 2103.457866152952, "total": 0.9351268839995726, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-316]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-316]", "params": { "numba": true, "log": true, "n": 316 }, "param": "True-True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004328759999907561, "max": 0.0010588549999965835, "mean": 0.0005392366092147661, "stddev": 0.00011845867843780345, "rounds": 1172, "median": 0.00047436049999305396, "iqr": 0.0001410179999936645, "q1": 0.00045806500000367123, "q3": 0.0005990829999973357, "iqr_outliers": 47, "stddev_outliers": 191, "outliers": "191;47", "ld15iqr": 0.0004328759999907561, "hd15iqr": 0.00081333899998981, "ops": 1854.4734962564867, "total": 0.6319853059997058, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-1000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-1000]", "params": { "numba": true, "log": true, "n": 1000 }, "param": "True-True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00043979399998761437, "max": 0.0013313549999907082, "mean": 0.0005350957644579634, "stddev": 0.00013075402531963296, "rounds": 1660, "median": 0.00047016950000511315, "iqr": 0.00013194699998564374, "q1": 0.0004477095000083864, "q3": 0.0005796564999940301, "iqr_outliers": 105, "stddev_outliers": 242, "outliers": "242;105", "ld15iqr": 0.00043979399998761437, "hd15iqr": 0.0007777449999935016, "ops": 1868.8243608374873, "total": 0.8882589690002192, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-3162]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-3162]", "params": { "numba": true, "log": true, "n": 3162 }, "param": "True-True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00046590800002377364, "max": 0.0010717620000093575, "mean": 0.0005515500011639458, "stddev": 0.00011455680487290001, "rounds": 1719, "median": 0.0004965059999904042, "iqr": 0.00012479274998611345, "q1": 0.00047282225000344624, "q3": 0.0005976149999895597, "iqr_outliers": 94, "stddev_outliers": 270, "outliers": "270;94", "ld15iqr": 0.00046590800002377364, "hd15iqr": 0.000787972000011905, "ops": 1813.0722471030408, "total": 0.9481144520008229, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-10000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-10000]", "params": { "numba": true, "log": true, "n": 10000 }, "param": "True-True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007520370000122512, "max": 0.0018421880000119017, "mean": 0.0009149970662489864, "stddev": 0.0001907750093467647, "rounds": 1117, "median": 0.000823462999989033, "iqr": 0.0002045154999876786, "q1": 0.0007893734999910862, "q3": 0.0009938889999787648, "iqr_outliers": 71, "stddev_outliers": 159, "outliers": "159;71", "ld15iqr": 0.0007520370000122512, "hd15iqr": 0.001302224999989221, "ops": 1092.8996790115202, "total": 1.0220517230001178, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-31622]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-31622]", "params": { "numba": true, "log": true, "n": 31622 }, "param": "True-True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0021368330000086644, "max": 0.003999949000018432, "mean": 0.0026084402773717845, "stddev": 0.00037383043636695523, "rounds": 411, "median": 0.0025082939999947484, "iqr": 0.000464154500001257, "q1": 0.0023317954999839685, "q3": 0.0027959499999852255, "iqr_outliers": 11, "stddev_outliers": 128, "outliers": "128;11", "ld15iqr": 0.0021368330000086644, "hd15iqr": 0.0035078079999948386, "ops": 383.37086291566595, "total": 1.0720689539998034, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-100000]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-100000]", "params": { "numba": true, "log": true, "n": 100000 }, "param": "True-True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004990885000012213, "max": 0.007896475999984887, "mean": 0.005972209641024269, "stddev": 0.0005900366215827988, "rounds": 156, "median": 0.005957894000005126, "iqr": 0.0007728875000054813, "q1": 0.005514118499988285, "q3": 0.006287005999993767, "iqr_outliers": 3, "stddev_outliers": 54, "outliers": "54;3", "ld15iqr": 0.004990885000012213, "hd15iqr": 0.007816135000012991, "ops": 167.44221320209618, "total": 0.9316647039997861, "iterations": 1 } }, { "group": null, "name": "test_minuit_custom[True-True-316227]", "fullname": "bench/test_cost.py::test_minuit_custom[True-True-316227]", "params": { "numba": true, "log": true, "n": 316227 }, "param": "True-True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.01949758699998938, "max": 0.027939285999991625, "mean": 0.021289639764706506, "stddev": 0.0017427119903350153, "rounds": 51, "median": 0.020739848999994592, "iqr": 0.0015701012499675926, "q1": 0.020108637500015902, "q3": 0.021678738749983495, "iqr_outliers": 5, "stddev_outliers": 9, "outliers": "9;5", "ld15iqr": 0.01949758699998938, "hd15iqr": 0.02410944599998288, "ops": 46.971203414055786, "total": 1.0857716280000318, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[10]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[10]", "params": { "n": 10 }, "param": "10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006447549999961666, "max": 0.0015930299999808994, "mean": 0.0007842420807876833, "stddev": 0.0001565645130149, "rounds": 1015, "median": 0.0006999699999994391, "iqr": 0.0001715167499725112, "q1": 0.0006756515000105878, "q3": 0.000847168249983099, "iqr_outliers": 61, "stddev_outliers": 147, "outliers": "147;61", "ld15iqr": 0.0006447549999961666, "hd15iqr": 0.0011045630000126039, "ops": 1275.1164780594431, "total": 0.7960057119994985, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[31]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[31]", "params": { "n": 31 }, "param": "31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004511540000180503, "max": 0.001408961999999292, "mean": 0.0005259686660196399, "stddev": 0.00010568538228575217, "rounds": 1545, "median": 0.00047737200000597113, "iqr": 9.500899999892454e-05, "q1": 0.0004575827500090668, "q3": 0.0005525917500079913, "iqr_outliers": 135, "stddev_outliers": 227, "outliers": "227;135", "ld15iqr": 0.0004511540000180503, "hd15iqr": 0.0006951860000015131, "ops": 1901.2539426876422, "total": 0.8126215890003436, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[100]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[100]", "params": { "n": 100 }, "param": "100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00045346900000708956, "max": 0.0013854369999819482, "mean": 0.000554866174465344, "stddev": 0.00013699137067027308, "rounds": 1731, "median": 0.00048286000000530294, "iqr": 0.00011051450000110208, "q1": 0.0004782202499953314, "q3": 0.0005887347499964335, "iqr_outliers": 179, "stddev_outliers": 264, "outliers": "264;179", "ld15iqr": 0.00045346900000708956, "hd15iqr": 0.0007564699999988989, "ops": 1802.2363698121922, "total": 0.9604733479995105, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[316]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[316]", "params": { "n": 316 }, "param": "316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004975850000050741, "max": 0.0015226000000154727, "mean": 0.0005956076732426517, "stddev": 0.00014400037339921823, "rounds": 1622, "median": 0.0005278789999891842, "iqr": 0.00012384700002598947, "q1": 0.0005052779999914492, "q3": 0.0006291250000174387, "iqr_outliers": 144, "stddev_outliers": 219, "outliers": "219;144", "ld15iqr": 0.0004975850000050741, "hd15iqr": 0.0008150140000111605, "ops": 1678.9575502876341, "total": 0.966075645999581, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[1000]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[1000]", "params": { "n": 1000 }, "param": "1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004520509999963451, "max": 0.0012797220000209109, "mean": 0.0005659766371160534, "stddev": 0.00014320245033526549, "rounds": 1692, "median": 0.0004979195000061054, "iqr": 0.000115224500021327, "q1": 0.00047633049999262767, "q3": 0.0005915550000139547, "iqr_outliers": 203, "stddev_outliers": 252, "outliers": "252;203", "ld15iqr": 0.0004520509999963451, "hd15iqr": 0.0007654270000045926, "ops": 1766.8573831872677, "total": 0.9576324700003624, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[3162]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[3162]", "params": { "n": 3162 }, "param": "3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00040641699999355296, "max": 0.0014257770000085657, "mean": 0.000509190936292067, "stddev": 0.00014181213943027505, "rounds": 1915, "median": 0.00043356099999414255, "iqr": 0.00011474950000689432, "q1": 0.00042845974999039527, "q3": 0.0005432092499972896, "iqr_outliers": 186, "stddev_outliers": 275, "outliers": "275;186", "ld15iqr": 0.00040641699999355296, "hd15iqr": 0.0007153869999854123, "ops": 1963.8998433122338, "total": 0.9751006429993083, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[10000]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[10000]", "params": { "n": 10000 }, "param": "10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00043939399998293993, "max": 0.0011787109999943368, "mean": 0.000532945294524735, "stddev": 0.00011164931986661854, "rounds": 1735, "median": 0.00048139100002231316, "iqr": 0.00010011325001357818, "q1": 0.00046285599998441285, "q3": 0.000562969249997991, "iqr_outliers": 167, "stddev_outliers": 290, "outliers": "290;167", "ld15iqr": 0.00043939399998293993, "hd15iqr": 0.0007132650000016838, "ops": 1876.3651922131537, "total": 0.924660086000415, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[31622]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[31622]", "params": { "n": 31622 }, "param": "31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.000692094999976689, "max": 0.0016611030000035498, "mean": 0.0008298776604989758, "stddev": 0.0001619579016758851, "rounds": 1081, "median": 0.0007468939999739632, "iqr": 0.00018415425002871189, "q1": 0.0007206242499862014, "q3": 0.0009047785000149133, "iqr_outliers": 50, "stddev_outliers": 177, "outliers": "177;50", "ld15iqr": 0.000692094999976689, "hd15iqr": 0.0011827239999888661, "ops": 1204.996890022001, "total": 0.8970977509993929, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[100000]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[100000]", "params": { "n": 100000 }, "param": "100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009025119999819253, "max": 0.0018434059999776764, "mean": 0.001093183470524098, "stddev": 0.00018948897883235276, "rounds": 916, "median": 0.0010175925000055486, "iqr": 0.00024105599999302285, "q1": 0.0009530935000015006, "q3": 0.0011941494999945235, "iqr_outliers": 29, "stddev_outliers": 141, "outliers": "141;29", "ld15iqr": 0.0009025119999819253, "hd15iqr": 0.0015616629999897214, "ops": 914.7595321035878, "total": 1.0013560590000736, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_sum_logpdf_parallel_fastmath[316227]", "fullname": "bench/test_cost.py::test_minuit_numba_sum_logpdf_parallel_fastmath[316227]", "params": { "n": 316227 }, "param": "316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0022540200000094046, "max": 0.004824810000002344, "mean": 0.0028190955069646068, "stddev": 0.00044183862244312174, "rounds": 359, "median": 0.0027121580000084577, "iqr": 0.0005951160000137179, "q1": 0.0024769562499855624, "q3": 0.0030720722499992803, "iqr_outliers": 10, "stddev_outliers": 96, "outliers": "96;10", "ld15iqr": 0.0022540200000094046, "hd15iqr": 0.00398796899997933, "ops": 354.72370394315794, "total": 1.012055287000294, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[10]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[10]", "params": { "n": 10 }, "param": "10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006169539999802964, "max": 0.0013400910000029853, "mean": 0.0007354356790245874, "stddev": 0.00013036012440391771, "rounds": 1025, "median": 0.0006735409999976127, "iqr": 0.00014174899999375157, "q1": 0.0006452930000051538, "q3": 0.0007870419999989053, "iqr_outliers": 60, "stddev_outliers": 158, "outliers": "158;60", "ld15iqr": 0.0006169539999802964, "hd15iqr": 0.0010006649999922956, "ops": 1359.7382184752116, "total": 0.7538215710002021, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[31]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[31]", "params": { "n": 31 }, "param": "31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00045015799997827344, "max": 0.0012866180000230543, "mean": 0.0005365436358944543, "stddev": 0.00012836141443097246, "rounds": 1744, "median": 0.00047820700000045235, "iqr": 0.00010994799998798044, "q1": 0.0004574595000121917, "q3": 0.0005674075000001721, "iqr_outliers": 136, "stddev_outliers": 221, "outliers": "221;136", "ld15iqr": 0.00045015799997827344, "hd15iqr": 0.0007330510000258528, "ops": 1863.7813089198849, "total": 0.9357321009999282, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[100]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[100]", "params": { "n": 100 }, "param": "100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00046435999999516753, "max": 0.0014027260000091246, "mean": 0.0005565170250606008, "stddev": 0.00013389062863757158, "rounds": 1237, "median": 0.0004945350000014059, "iqr": 0.00011656950001537325, "q1": 0.0004753962499890463, "q3": 0.0005919657500044195, "iqr_outliers": 84, "stddev_outliers": 146, "outliers": "146;84", "ld15iqr": 0.00046435999999516753, "hd15iqr": 0.0007677450000187491, "ops": 1796.890220727941, "total": 0.6884115599999632, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[316]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[316]", "params": { "n": 316 }, "param": "316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004767350000065562, "max": 0.0010502730000041538, "mean": 0.0005557864736837383, "stddev": 9.341290116534625e-05, "rounds": 1615, "median": 0.0005062649999842961, "iqr": 6.809850000877304e-05, "q1": 0.000500083499993309, "q3": 0.0005681820000020821, "iqr_outliers": 217, "stddev_outliers": 259, "outliers": "259;217", "ld15iqr": 0.0004767350000065562, "hd15iqr": 0.0006703820000097949, "ops": 1799.2521361162785, "total": 0.8975951549992374, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[1000]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[1000]", "params": { "n": 1000 }, "param": "1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004586219999964669, "max": 0.001172613999983696, "mean": 0.0005524427758412109, "stddev": 0.00011277084009062057, "rounds": 1664, "median": 0.0005010604999853285, "iqr": 9.5973499981028e-05, "q1": 0.0004800764999970397, "q3": 0.0005760499999780677, "iqr_outliers": 179, "stddev_outliers": 269, "outliers": "269;179", "ld15iqr": 0.0004586219999964669, "hd15iqr": 0.0007208290000164652, "ops": 1810.1422332426894, "total": 0.919264778999775, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[3162]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[3162]", "params": { "n": 3162 }, "param": "3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00038899599999808743, "max": 0.001106687999993028, "mean": 0.0004695797848955093, "stddev": 0.00010632279095631717, "rounds": 1920, "median": 0.000415307500006179, "iqr": 7.069250000313332e-05, "q1": 0.00040906750000146985, "q3": 0.00047976000000460317, "iqr_outliers": 260, "stddev_outliers": 275, "outliers": "275;260", "ld15iqr": 0.00038899599999808743, "hd15iqr": 0.0005861009999819089, "ops": 2129.563563351688, "total": 0.9015931869993778, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[10000]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[10000]", "params": { "n": 10000 }, "param": "10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004541559999893252, "max": 0.001393481999997448, "mean": 0.000576431029412094, "stddev": 0.00016425272729781457, "rounds": 816, "median": 0.00048226399999862224, "iqr": 0.00015625100000704606, "q1": 0.00047502050000503004, "q3": 0.0006312715000120761, "iqr_outliers": 66, "stddev_outliers": 127, "outliers": "127;66", "ld15iqr": 0.0004541559999893252, "hd15iqr": 0.0008694260000083887, "ops": 1734.812924661441, "total": 0.4703677200002687, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[31622]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[31622]", "params": { "n": 31622 }, "param": "31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006344500000068365, "max": 0.0016135670000210212, "mean": 0.0007772023883264803, "stddev": 0.00017813848871515344, "rounds": 1285, "median": 0.0006859529999871938, "iqr": 0.0001660530000222593, "q1": 0.0006688909999894577, "q3": 0.000834944000011717, "iqr_outliers": 110, "stddev_outliers": 185, "outliers": "185;110", "ld15iqr": 0.0006344500000068365, "hd15iqr": 0.0010847110000042903, "ops": 1286.6661438769652, "total": 0.9987050689995272, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[100000]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[100000]", "params": { "n": 100000 }, "param": "100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008010539999929733, "max": 0.0022210260000008475, "mean": 0.0010672760456803586, "stddev": 0.0002877242619050517, "rounds": 1007, "median": 0.0009236479999970015, "iqr": 0.0003022327500019628, "q1": 0.00088111500000565, "q3": 0.0011833477500076128, "iqr_outliers": 74, "stddev_outliers": 160, "outliers": "160;74", "ld15iqr": 0.0008010539999929733, "hd15iqr": 0.0016387120000160849, "ops": 936.9647187785687, "total": 1.074746978000121, "iterations": 1 } }, { "group": null, "name": "test_minuit_numba_handtuned_parallel_fastmath[316227]", "fullname": "bench/test_cost.py::test_minuit_numba_handtuned_parallel_fastmath[316227]", "params": { "n": 316227 }, "param": "316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0018499210000015864, "max": 0.004200620999995408, "mean": 0.0022775608804356355, "stddev": 0.00037805549420293413, "rounds": 460, "median": 0.002145488500005399, "iqr": 0.0002891255000037063, "q1": 0.002060043000000178, "q3": 0.0023491685000038842, "iqr_outliers": 36, "stddev_outliers": 55, "outliers": "55;36", "ld15iqr": 0.0018499210000015864, "hd15iqr": 0.0027852329999973335, "ops": 439.06619954269985, "total": 1.0476780050003924, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[10]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[10]", "params": { "n": 10 }, "param": "10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00028420900000014626, "max": 0.0008445459999961713, "mean": 0.0003479935256570441, "stddev": 8.193483762795691e-05, "rounds": 2397, "median": 0.00031339099999172504, "iqr": 4.8590500014711324e-05, "q1": 0.0002999247500028446, "q3": 0.0003485152500175559, "iqr_outliers": 348, "stddev_outliers": 321, "outliers": "321;348", "ld15iqr": 0.00028420900000014626, "hd15iqr": 0.00042177200000992343, "ops": 2873.616680402048, "total": 0.8341404809999347, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[31]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[31]", "params": { "n": 31 }, "param": "31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0002551640000092448, "max": 0.0010490439999841783, "mean": 0.0003081439760683613, "stddev": 7.846546720246586e-05, "rounds": 2340, "median": 0.0002789315000057968, "iqr": 3.9101999988133684e-05, "q1": 0.0002670349999931432, "q3": 0.00030613699998127686, "iqr_outliers": 340, "stddev_outliers": 287, "outliers": "287;340", "ld15iqr": 0.0002551640000092448, "hd15iqr": 0.0003652430000045115, "ops": 3245.23624559888, "total": 0.7210569039999655, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[100]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[100]", "params": { "n": 100 }, "param": "100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.000267385000000786, "max": 0.0008799059999944348, "mean": 0.0003238323639140989, "stddev": 8.990359949664036e-05, "rounds": 2616, "median": 0.0002826215000055754, "iqr": 6.883249997713392e-05, "q1": 0.0002701845000103731, "q3": 0.000339016999987507, "iqr_outliers": 271, "stddev_outliers": 367, "outliers": "367;271", "ld15iqr": 0.000267385000000786, "hd15iqr": 0.00044231399999716814, "ops": 3088.017478899249, "total": 0.8471454639992828, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[316]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[316]", "params": { "n": 316 }, "param": "316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0002733890000001793, "max": 0.0008168150000074093, "mean": 0.0003298011725664967, "stddev": 7.497849278677978e-05, "rounds": 2486, "median": 0.00029053299999759474, "iqr": 5.076999997299936e-05, "q1": 0.0002866750000123375, "q3": 0.00033744499998533684, "iqr_outliers": 324, "stddev_outliers": 355, "outliers": "355;324", "ld15iqr": 0.0002733890000001793, "hd15iqr": 0.0004142679999858956, "ops": 3032.12991093406, "total": 0.8198857150003107, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[1000]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[1000]", "params": { "n": 1000 }, "param": "1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00030172299997843766, "max": 0.0008848319999970045, "mean": 0.0003798759886840202, "stddev": 9.481835510859718e-05, "rounds": 2386, "median": 0.00032567249999715386, "iqr": 0.00010039599999345228, "q1": 0.0003187549999950079, "q3": 0.00041915099998846017, "iqr_outliers": 154, "stddev_outliers": 376, "outliers": "376;154", "ld15iqr": 0.00030172299997843766, "hd15iqr": 0.000570160000023634, "ops": 2632.43803185412, "total": 0.9063841090000722, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[3162]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[3162]", "params": { "n": 3162 }, "param": "3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.00035379999999918255, "max": 0.0008948260000067876, "mean": 0.0004093662588432514, "stddev": 8.74659813714349e-05, "rounds": 1781, "median": 0.0003644489999885536, "iqr": 6.840875002467328e-05, "q1": 0.00035715699998917216, "q3": 0.00042556575001384545, "iqr_outliers": 229, "stddev_outliers": 289, "outliers": "289;229", "ld15iqr": 0.00035379999999918255, "hd15iqr": 0.0005282380000153353, "ops": 2442.8002513585407, "total": 0.7290813069998308, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[10000]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[10000]", "params": { "n": 10000 }, "param": "10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006233320000035292, "max": 0.0016100530000073832, "mean": 0.0007169718373211462, "stddev": 0.00014036103764686689, "rounds": 1254, "median": 0.0006401134999975966, "iqr": 0.00014293599997472484, "q1": 0.0006274890000099731, "q3": 0.0007704249999846979, "iqr_outliers": 97, "stddev_outliers": 214, "outliers": "214;97", "ld15iqr": 0.0006233320000035292, "hd15iqr": 0.000985245000009627, "ops": 1394.754923340287, "total": 0.8990826840007173, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[31622]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[31622]", "params": { "n": 31622 }, "param": "31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0017984259999934693, "max": 0.0031719259999931637, "mean": 0.0022157871553666916, "stddev": 0.00030705215548166345, "rounds": 354, "median": 0.0021333315000049424, "iqr": 0.0004940940000039973, "q1": 0.0019429609999974673, "q3": 0.0024370550000014646, "iqr_outliers": 0, "stddev_outliers": 121, "outliers": "121;0", "ld15iqr": 0.0017984259999934693, "hd15iqr": 0.0031719259999931637, "ops": 451.30688549122385, "total": 0.7843886529998088, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[100000]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[100000]", "params": { "n": 100000 }, "param": "100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004520018999983222, "max": 0.008775570000011612, "mean": 0.005380861803278567, "stddev": 0.0006082198985246072, "rounds": 183, "median": 0.005286898999997902, "iqr": 0.0005016057499744875, "q1": 0.00504591650001629, "q3": 0.005547522249990777, "iqr_outliers": 10, "stddev_outliers": 30, "outliers": "30;10", "ld15iqr": 0.004520018999983222, "hd15iqr": 0.006321036999992202, "ops": 185.84383627743398, "total": 0.9846977099999776, "iterations": 1 } }, { "group": null, "name": "test_minuit_cfunc_sum_logpdf[316227]", "fullname": "bench/test_cost.py::test_minuit_cfunc_sum_logpdf[316227]", "params": { "n": 316227 }, "param": "316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.01902835599997843, "max": 0.023534320999999636, "mean": 0.02005255944680737, "stddev": 0.0008857244094260912, "rounds": 47, "median": 0.019740104000021574, "iqr": 0.0009997244999908617, "q1": 0.01945873849999913, "q3": 0.02045846299998999, "iqr_outliers": 2, "stddev_outliers": 10, "outliers": "10;2", "ld15iqr": 0.01902835599997843, "hd15iqr": 0.02198451499998555, "ops": 49.86894578982102, "total": 0.9424702939999463, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-10]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-10]", "params": { "log": false, "n": 10 }, "param": "False-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009500509999895712, "max": 0.0021751660000006723, "mean": 0.001163103485426069, "stddev": 0.0002163732143692876, "rounds": 892, "median": 0.001063496999975655, "iqr": 0.0002513774999783891, "q1": 0.0009969680000097014, "q3": 0.0012483454999880905, "iqr_outliers": 43, "stddev_outliers": 151, "outliers": "151;43", "ld15iqr": 0.0009500509999895712, "hd15iqr": 0.0016263640000033774, "ops": 859.768724391432, "total": 1.0374883090000537, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-31]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-31]", "params": { "log": false, "n": 31 }, "param": "False-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006618170000081136, "max": 0.0013512219999824993, "mean": 0.0007760414820512503, "stddev": 0.00012780869805370098, "rounds": 1170, "median": 0.0007192424999971081, "iqr": 0.00013883399998348978, "q1": 0.0006903830000055677, "q3": 0.0008292169999890575, "iqr_outliers": 64, "stddev_outliers": 184, "outliers": "184;64", "ld15iqr": 0.0006618170000081136, "hd15iqr": 0.0010405230000003485, "ops": 1288.5909105744934, "total": 0.9079685339999628, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-100]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-100]", "params": { "log": false, "n": 100 }, "param": "False-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006952620000220122, "max": 0.0014686420000202816, "mean": 0.0008397515719299828, "stddev": 0.00015518254979093076, "rounds": 855, "median": 0.0007648550000283194, "iqr": 0.00018832924998690714, "q1": 0.0007313414999998713, "q3": 0.0009196707499867784, "iqr_outliers": 30, "stddev_outliers": 160, "outliers": "160;30", "ld15iqr": 0.0006952620000220122, "hd15iqr": 0.001216068000019277, "ops": 1190.82837523212, "total": 0.7179875940001352, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-316]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-316]", "params": { "log": false, "n": 316 }, "param": "False-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008643740000024991, "max": 0.001995728999986568, "mean": 0.0010439097798508438, "stddev": 0.00020806135760371145, "rounds": 804, "median": 0.0009451905000048555, "iqr": 0.00019835750001107044, "q1": 0.0009114770000024919, "q3": 0.0011098345000135623, "iqr_outliers": 67, "stddev_outliers": 125, "outliers": "125;67", "ld15iqr": 0.0008643740000024991, "hd15iqr": 0.0014084320000051775, "ops": 957.9371889234358, "total": 0.8393034630000784, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-1000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-1000]", "params": { "log": false, "n": 1000 }, "param": "False-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0010225190000028306, "max": 0.0026591009999776816, "mean": 0.0012434683702419432, "stddev": 0.0002772054801049433, "rounds": 867, "median": 0.0011282440000002225, "iqr": 0.00020816350001950923, "q1": 0.0010788477500014437, "q3": 0.001287011250020953, "iqr_outliers": 97, "stddev_outliers": 111, "outliers": "111;97", "ld15iqr": 0.0010225190000028306, "hd15iqr": 0.0016033560000039415, "ops": 804.2022008211022, "total": 1.0780870769997648, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-3162]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-3162]", "params": { "log": false, "n": 3162 }, "param": "False-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.001305465999990929, "max": 0.0028778389999786214, "mean": 0.0015743717098670453, "stddev": 0.00031583569333025095, "rounds": 679, "median": 0.0014391480000028878, "iqr": 0.000270756250003501, "q1": 0.0013738869999997405, "q3": 0.0016446432500032415, "iqr_outliers": 71, "stddev_outliers": 96, "outliers": "96;71", "ld15iqr": 0.001305465999990929, "hd15iqr": 0.0020530059999828154, "ops": 635.1740149627367, "total": 1.0689983909997238, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-10000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-10000]", "params": { "log": false, "n": 10000 }, "param": "False-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.003588719000021001, "max": 0.006967398000000458, "mean": 0.004346541895197051, "stddev": 0.0006198113371817329, "rounds": 229, "median": 0.004060890000005202, "iqr": 0.0009249262500006239, "q1": 0.003895881749997443, "q3": 0.004820807999998067, "iqr_outliers": 4, "stddev_outliers": 47, "outliers": "47;4", "ld15iqr": 0.003588719000021001, "hd15iqr": 0.006245973999995158, "ops": 230.06795381519382, "total": 0.9953580940001245, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-31622]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-31622]", "params": { "log": false, "n": 31622 }, "param": "False-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.012513622000000169, "max": 0.01677481799998759, "mean": 0.014605974288135655, "stddev": 0.0009040166266761113, "rounds": 59, "median": 0.014678333000006205, "iqr": 0.0013619759999912162, "q1": 0.01391518524999924, "q3": 0.015277161249990456, "iqr_outliers": 0, "stddev_outliers": 19, "outliers": "19;0", "ld15iqr": 0.012513622000000169, "hd15iqr": 0.01677481799998759, "ops": 68.46513490115439, "total": 0.8617524830000036, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-100000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-100000]", "params": { "log": false, "n": 100000 }, "param": "False-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.032298643999979504, "max": 0.037699446000004855, "mean": 0.03393516844827728, "stddev": 0.0009654127119240813, "rounds": 29, "median": 0.03381888599997751, "iqr": 0.0006604002500054662, "q1": 0.03351382150000859, "q3": 0.034174221750014055, "iqr_outliers": 2, "stddev_outliers": 6, "outliers": "6;2", "ld15iqr": 0.03256087400001206, "hd15iqr": 0.037699446000004855, "ops": 29.46795450637479, "total": 0.984119885000041, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[False-316227]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[False-316227]", "params": { "log": false, "n": 316227 }, "param": "False-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.12719934200001148, "max": 0.13136711900000364, "mean": 0.12872532262500158, "stddev": 0.0013783641940866545, "rounds": 8, "median": 0.12850404099999935, "iqr": 0.0015317520000053264, "q1": 0.12779113349999704, "q3": 0.12932288550000237, "iqr_outliers": 0, "stddev_outliers": 3, "outliers": "3;0", "ld15iqr": 0.12719934200001148, "hd15iqr": 0.13136711900000364, "ops": 7.768479267386787, "total": 1.0298025810000127, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-10]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-10]", "params": { "log": true, "n": 10 }, "param": "True-10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0013268200000027264, "max": 0.0027703669999823433, "mean": 0.0015558359545456246, "stddev": 0.0002398404314984047, "rounds": 550, "median": 0.0014608724999902734, "iqr": 0.0002616170000067086, "q1": 0.0013864539999985936, "q3": 0.0016480710000053023, "iqr_outliers": 32, "stddev_outliers": 81, "outliers": "81;32", "ld15iqr": 0.0013268200000027264, "hd15iqr": 0.002041799000011224, "ops": 642.7412845668847, "total": 0.8557097750000935, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-31]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-31]", "params": { "log": true, "n": 31 }, "param": "True-31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008524710000017421, "max": 0.0023565159999918706, "mean": 0.001070813328124783, "stddev": 0.0002734924517447499, "rounds": 960, "median": 0.0009472470000133626, "iqr": 0.00023539750000622917, "q1": 0.0009057919999975184, "q3": 0.0011411895000037475, "iqr_outliers": 83, "stddev_outliers": 127, "outliers": "127;83", "ld15iqr": 0.0008524710000017421, "hd15iqr": 0.0014967170000090846, "ops": 933.8695865424164, "total": 1.0279807949997917, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-100]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-100]", "params": { "log": true, "n": 100 }, "param": "True-100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0008614839999836477, "max": 0.002530946999996786, "mean": 0.0010834494570856042, "stddev": 0.00024397184166669071, "rounds": 1002, "median": 0.0009715845000073386, "iqr": 0.00025357599997732905, "q1": 0.0009158640000066498, "q3": 0.0011694399999839789, "iqr_outliers": 62, "stddev_outliers": 141, "outliers": "141;62", "ld15iqr": 0.0008614839999836477, "hd15iqr": 0.0015542489999802456, "ops": 922.9779879994801, "total": 1.0856163559997754, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-316]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-316]", "params": { "log": true, "n": 316 }, "param": "True-316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009960589999877811, "max": 0.003216543999997157, "mean": 0.001349540059023644, "stddev": 0.00036189012498504935, "rounds": 881, "median": 0.0012199869999847124, "iqr": 0.00043317675000054123, "q1": 0.0010779427499869598, "q3": 0.001511119499987501, "iqr_outliers": 34, "stddev_outliers": 123, "outliers": "123;34", "ld15iqr": 0.0009960589999877811, "hd15iqr": 0.0021940579999863985, "ops": 740.9931949137347, "total": 1.1889447919998304, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-1000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-1000]", "params": { "log": true, "n": 1000 }, "param": "True-1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0009383240000033766, "max": 0.0024742819999801213, "mean": 0.0012518140994157772, "stddev": 0.0003125229195674502, "rounds": 513, "median": 0.0011416589999839744, "iqr": 0.00033568399999239773, "q1": 0.0010188847500032239, "q3": 0.0013545687499956216, "iqr_outliers": 36, "stddev_outliers": 75, "outliers": "75;36", "ld15iqr": 0.0009383240000033766, "hd15iqr": 0.0018581129999972745, "ops": 798.8406589019096, "total": 0.6421806330002937, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-3162]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-3162]", "params": { "log": true, "n": 3162 }, "param": "True-3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0007931719999874076, "max": 0.0022585739999954058, "mean": 0.0010533879853709153, "stddev": 0.00027790488354070586, "rounds": 957, "median": 0.0009487769999907414, "iqr": 0.00028883100001309003, "q1": 0.0008526302500015959, "q3": 0.001141461250014686, "iqr_outliers": 68, "stddev_outliers": 130, "outliers": "130;68", "ld15iqr": 0.0007931719999874076, "hd15iqr": 0.0015812840000251072, "ops": 949.3178333981886, "total": 1.008092301999966, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-10000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-10000]", "params": { "log": true, "n": 10000 }, "param": "True-10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0011688759999799458, "max": 0.002916024999990441, "mean": 0.001509200726087309, "stddev": 0.0003141009604528609, "rounds": 690, "median": 0.0014224404999936269, "iqr": 0.00037800399999809997, "q1": 0.0012661419999915324, "q3": 0.0016441459999896324, "iqr_outliers": 32, "stddev_outliers": 108, "outliers": "108;32", "ld15iqr": 0.0011688759999799458, "hd15iqr": 0.002214564000013297, "ops": 662.6023846361103, "total": 1.0413485010002432, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-31622]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-31622]", "params": { "log": true, "n": 31622 }, "param": "True-31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0024861429999987195, "max": 0.004981973000013795, "mean": 0.0030995408549860473, "stddev": 0.0004135482973252876, "rounds": 331, "median": 0.0030435200000056284, "iqr": 0.0005594307500089712, "q1": 0.0027622864999941044, "q3": 0.0033217172500030756, "iqr_outliers": 4, "stddev_outliers": 110, "outliers": "110;4", "ld15iqr": 0.0024861429999987195, "hd15iqr": 0.004232782000002544, "ops": 322.6284300758157, "total": 1.0259480230003817, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-100000]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-100000]", "params": { "log": true, "n": 100000 }, "param": "True-100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.004802079000000958, "max": 0.013973354000000882, "mean": 0.006184715731707305, "stddev": 0.00131114337082781, "rounds": 164, "median": 0.005878464999995003, "iqr": 0.001183939500009501, "q1": 0.005392125499994904, "q3": 0.006576065000004405, "iqr_outliers": 8, "stddev_outliers": 16, "outliers": "16;8", "ld15iqr": 0.004802079000000958, "hd15iqr": 0.00848663800002214, "ops": 161.6889188412137, "total": 1.014293379999998, "iterations": 1 } }, { "group": null, "name": "test_minuit_UnbinnedNLL[True-316227]", "fullname": "bench/test_cost.py::test_minuit_UnbinnedNLL[True-316227]", "params": { "log": true, "n": 316227 }, "param": "True-316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.02254614800000354, "max": 0.03322082799999748, "mean": 0.025007343810812387, "stddev": 0.002478186561594418, "rounds": 37, "median": 0.024372866999982534, "iqr": 0.0023638517500330636, "q1": 0.023315686249979706, "q3": 0.02567953800001277, "iqr_outliers": 3, "stddev_outliers": 4, "outliers": "4;3", "ld15iqr": 0.02254614800000354, "hd15iqr": 0.030401066000024457, "ops": 39.98825335330622, "total": 0.9252717210000583, "iterations": 1 } }, { "group": null, "name": "test_RooFit[10]", "fullname": "bench/test_cost.py::test_RooFit[10]", "params": { "n": 10 }, "param": "10", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006122679999975844, "max": 0.0009010500000101729, "mean": 0.0007050892857145234, "stddev": 0.00010758060558618865, "rounds": 14, "median": 0.0006504324999951905, "iqr": 0.0001380970000184334, "q1": 0.0006222669999829122, "q3": 0.0007603640000013456, "iqr_outliers": 0, "stddev_outliers": 3, "outliers": "3;0", "ld15iqr": 0.0006122679999975844, "hd15iqr": 0.0009010500000101729, "ops": 1418.2600987712071, "total": 0.009871250000003329, "iterations": 1 } }, { "group": null, "name": "test_RooFit[31]", "fullname": "bench/test_cost.py::test_RooFit[31]", "params": { "n": 31 }, "param": "31", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0004892839999968146, "max": 0.0015550309999809997, "mean": 0.0005778007936199647, "stddev": 0.00010770752459401756, "rounds": 1536, "median": 0.0005270945000006577, "iqr": 0.00010376050002491866, "q1": 0.0005090164999899116, "q3": 0.0006127770000148303, "iqr_outliers": 121, "stddev_outliers": 230, "outliers": "230;121", "ld15iqr": 0.0004892839999968146, "hd15iqr": 0.0007688059999964025, "ops": 1730.7002881303192, "total": 0.8875020190002658, "iterations": 1 } }, { "group": null, "name": "test_RooFit[100]", "fullname": "bench/test_cost.py::test_RooFit[100]", "params": { "n": 100 }, "param": "100", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0006665639999994255, "max": 0.0012955530000056115, "mean": 0.0007882812602365763, "stddev": 0.00012215875083451973, "rounds": 1099, "median": 0.0007301910000023781, "iqr": 0.00015063824998406972, "q1": 0.0007054642500179398, "q3": 0.0008561025000020095, "iqr_outliers": 41, "stddev_outliers": 184, "outliers": "184;41", "ld15iqr": 0.0006665639999994255, "hd15iqr": 0.0010834070000100837, "ops": 1268.5827387294269, "total": 0.8663211049999973, "iterations": 1 } }, { "group": null, "name": "test_RooFit[316]", "fullname": "bench/test_cost.py::test_RooFit[316]", "params": { "n": 316 }, "param": "316", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0011175200000081986, "max": 0.00218862200000558, "mean": 0.0012993575508343548, "stddev": 0.00017844516878629842, "rounds": 659, "median": 0.0012324679999835553, "iqr": 0.0001928562499955433, "q1": 0.0011757307499991043, "q3": 0.0013685869999946476, "iqr_outliers": 37, "stddev_outliers": 98, "outliers": "98;37", "ld15iqr": 0.0011175200000081986, "hd15iqr": 0.0016609360000074957, "ops": 769.6111046246442, "total": 0.8562766259998398, "iterations": 1 } }, { "group": null, "name": "test_RooFit[1000]", "fullname": "bench/test_cost.py::test_RooFit[1000]", "params": { "n": 1000 }, "param": "1000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0023462180000137778, "max": 0.0036021269999935157, "mean": 0.0027190710953850436, "stddev": 0.0002960786807391795, "rounds": 325, "median": 0.002624744999991435, "iqr": 0.00035847350000750566, "q1": 0.0025032794999972907, "q3": 0.0028617530000047964, "iqr_outliers": 12, "stddev_outliers": 93, "outliers": "93;12", "ld15iqr": 0.0023462180000137778, "hd15iqr": 0.0034125629999834928, "ops": 367.7726565139303, "total": 0.8836981060001392, "iterations": 1 } }, { "group": null, "name": "test_RooFit[3162]", "fullname": "bench/test_cost.py::test_RooFit[3162]", "params": { "n": 3162 }, "param": "3162", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0070529690000000755, "max": 0.009920332999996617, "mean": 0.008050764816514068, "stddev": 0.0005535654462759453, "rounds": 109, "median": 0.008048930999990489, "iqr": 0.000883497000010891, "q1": 0.007585992499997474, "q3": 0.008469489500008365, "iqr_outliers": 1, "stddev_outliers": 42, "outliers": "42;1", "ld15iqr": 0.0070529690000000755, "hd15iqr": 0.009920332999996617, "ops": 124.21180133703047, "total": 0.8775333650000334, "iterations": 1 } }, { "group": null, "name": "test_RooFit[10000]", "fullname": "bench/test_cost.py::test_RooFit[10000]", "params": { "n": 10000 }, "param": "10000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.0241574460000038, "max": 0.02698788999998669, "mean": 0.02565058709090677, "stddev": 0.0007058901130612385, "rounds": 33, "median": 0.025619065999990198, "iqr": 0.000895872749978821, "q1": 0.025234184500000367, "q3": 0.02613005724997919, "iqr_outliers": 0, "stddev_outliers": 12, "outliers": "12;0", "ld15iqr": 0.0241574460000038, "hd15iqr": 0.02698788999998669, "ops": 38.98546245573084, "total": 0.8464693739999234, "iterations": 1 } }, { "group": null, "name": "test_RooFit[31622]", "fullname": "bench/test_cost.py::test_RooFit[31622]", "params": { "n": 31622 }, "param": "31622", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.06533745100000488, "max": 0.07018297199999779, "mean": 0.06782916825000267, "stddev": 0.0017636218885062626, "rounds": 12, "median": 0.06757253799999319, "iqr": 0.0033303670000179864, "q1": 0.06616526499999509, "q3": 0.06949563200001307, "iqr_outliers": 0, "stddev_outliers": 5, "outliers": "5;0", "ld15iqr": 0.06533745100000488, "hd15iqr": 0.07018297199999779, "ops": 14.742919982657472, "total": 0.8139500190000319, "iterations": 1 } }, { "group": null, "name": "test_RooFit[100000]", "fullname": "bench/test_cost.py::test_RooFit[100000]", "params": { "n": 100000 }, "param": "100000", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.23750098700000422, "max": 0.2524962930000072, "mean": 0.24434892280000325, "stddev": 0.00610208358833709, "rounds": 5, "median": 0.24614085199999636, "iqr": 0.00932900350000665, "q1": 0.2386875890000013, "q3": 0.24801659250000796, "iqr_outliers": 0, "stddev_outliers": 2, "outliers": "2;0", "ld15iqr": 0.23750098700000422, "hd15iqr": 0.2524962930000072, "ops": 4.09250832187416, "total": 1.2217446140000163, "iterations": 1 } }, { "group": null, "name": "test_RooFit[316227]", "fullname": "bench/test_cost.py::test_RooFit[316227]", "params": { "n": 316227 }, "param": "316227", "extra_info": {}, "options": { "disable_gc": false, "timer": "perf_counter", "min_rounds": 5, "max_time": 1.0, "min_time": 5e-06, "warmup": false }, "stats": { "min": 0.7698972659999868, "max": 0.7902549499999907, "mean": 0.7843643233999898, "stddev": 0.008318801216263792, "rounds": 5, "median": 0.7876884430000075, "iqr": 0.008084058500003266, "q1": 0.7812198569999822, "q3": 0.7893039154999855, "iqr_outliers": 0, "stddev_outliers": 1, "outliers": "1;0", "ld15iqr": 0.7698972659999868, "hd15iqr": 0.7902549499999907, "ops": 1.2749177520788968, "total": 3.9218216169999494, "iterations": 1 } } ], "datetime": "2022-03-15T15:06:02.637206", "version": "3.4.1" } iminuit-2.24.0/bench/plot.ipynb0000644000000000000000000001062114332717401013305 0ustar00{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import json\n", "import sys\n", "\n", "if len(sys.argv) == 2:\n", " fn = sys.argv[1]\n", "else:\n", " from pathlib import Path\n", "\n", " paths = []\n", " for p in (Path(\"..\") / \".benchmarks\").rglob(\"*.json\"):\n", " paths.append((p.stat().st_mtime, p))\n", " paths.sort()\n", " fn = paths[-1][1]\n", "\n", "with open(fn) as f:\n", " data = json.load(f)\n", "\n", "print(\n", " f\"\"\"\\\n", "benchmark results\n", " {data['datetime']}\n", " {data['machine_info']['cpu']['brand_raw']}\n", "\"\"\"\n", ")\n", "\n", "variant = {}\n", "for b in data[\"benchmarks\"]:\n", " params = b[\"params\"]\n", " n = params[\"n\"]\n", " n = int(n)\n", " name = b[\"name\"]\n", " name = name[name.find(\"_\") + 1 : name.find(\"[\")]\n", " extra = [k for (k, v) in params.items() if k not in (\"n\", \"lib\", \"model\") and v]\n", " if extra:\n", " name += \"_\" + \"_\".join(extra)\n", " for key in (\"lib\", \"model\"):\n", " if key in params:\n", " name += f\"_{params[key]}\"\n", " t = b[\"stats\"][\"min\"]\n", " if name not in variant:\n", " variant[name] = []\n", " variant[name].append((n, t))\n", "\n", "for k in variant:\n", " print(k)\n", "\n", "names = {\n", " \"Numba vs. SciPy\": {\n", " \"nll_scipy.stats\": \"handwritten NLL scipy.stats\",\n", " \"nll_numba_stats\": \"handwritten NLL numba_stats\",\n", " },\n", " \"Overhead\": {\n", " \"minuit_UnbinnedNLL\": \"iminuit+UnbinnedNLL\",\n", " \"minuit_numba\": \"iminuit+numba\",\n", " \"minuit_cfunc\": \"iminuit+numba.cfunc\",\n", " },\n", " \"RooFit vs. iminuit+numba\": {\n", " \"RooFit\": \"RooFit\",\n", " \"RooFit_BatchMode\": \"RooFit with BatchMode\",\n", " \"RooFit_NumCPU\": \"RooFit with NumCPU\",\n", " \"minuit\": \"iminuit+numba\",\n", " \"minuit_parallel_fastmath\": \"iminuit+numba with parallel, fastmath\",\n", " \"minuit_parallel_fastmath_log\": \"iminuit+numba logpdf with parallel, fastmath\",\n", " },\n", "}\n", "\n", "for title, subnames in names.items():\n", " plt.figure(constrained_layout=True)\n", " plt.title(title)\n", " for name in subnames:\n", " if name not in variant:\n", " continue\n", " d = variant[name]\n", " n, t = np.transpose(d)\n", " ls = \"-\"\n", " if (\n", " \"parallel\" in name\n", " and \"fastmath\" in name\n", " or (\"NumCPU\" in name and \"BatchMode\" in name)\n", " ):\n", " ls = \"-.\"\n", " elif \"parallel\" in name or \"NumCPU\" in name:\n", " ls = \"--\"\n", " elif \"fastmath\" in name or \"BatchMode\" in name:\n", " ls = \":\"\n", " plt.plot(n, t, ls=ls, label=subnames[name])\n", "\n", " plt.loglog()\n", " plt.legend(\n", " frameon=False,\n", " fontsize=\"medium\" if len(subnames) < 4 else \"small\",\n", " ncol=1 if len(subnames) < 3 else 2,\n", " )\n", " # plt.title(\"Fit of normal distribution with 2 parameters\")\n", " plt.xlabel(\"number of data points\")\n", " plt.ylabel(\"runtime / sec\")\n", " fname = title.replace(\" \", \"_\").replace(\".\", \"_\").replace(\"__\", \"_\").lower()\n", " plt.savefig(f\"{fname}.svg\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "py39", "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.9.16" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/bench/test_cost.py0000644000000000000000000001431714332717401013653 0ustar00from iminuit.cost import UnbinnedNLL from iminuit import Minuit import numpy as np import numba as nb from numba_stats import norm, truncexpon import pytest from numpy.testing import assert_allclose N = [int(x) for x in np.geomspace(10, 1e6, 11)] N = N[:-1] def make_data(size, seed=1): rng = np.random.default_rng(seed) s = rng.normal(0.5, 0.1, size=size // 2) b = rng.exponential(1, size=2 * size) b = b[b < 1] b = b[: size // 2] x = np.append(s, b) return x # we inline following functions to profit from parallel=True,fastmath=True @nb.njit(inline="always") def mixture(x, z, mu, sigma, slope): b = truncexpon.pdf(x, 0.0, 1.0, 0.0, slope) s = norm.pdf(x, mu, sigma) return (1 - z) * b + z * s @nb.njit(inline="always") def logsumexp(a, b): r = np.empty_like(a) for i in nb.prange(len(r)): if a[i] > b[i]: r[i] = a[i] + np.log(1 + np.exp(b[i] - a[i])) else: r[i] = b[i] + np.log(1 + np.exp(a[i] - b[i])) return r ARGS = (0.5, 0.5, 0.1, 1.0) @nb.njit(inline="always") def log_mixture(x, z, mu, sigma, slope): log_b = truncexpon.logpdf(x, 0.0, 1.0, 0.0, slope) log_s = norm.logpdf(x, mu, sigma) b = np.log(1 - z) + log_b s = np.log(z) + log_s return logsumexp(b, s) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("log", [False, True]) def test_UnbinnedNLL(benchmark, n, log): x = make_data(size=n) fn = log_mixture if log else mixture fn(x, *ARGS) # warm-up JIT cost = UnbinnedNLL(x, fn, log=log) benchmark(cost, *ARGS) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("lib", ["numba_stats", "scipy.stats"]) def test_nll(benchmark, n, lib): x = make_data(size=n) if lib == "scipy.stats": from scipy.stats import truncexpon, norm def fn(x, z, mu, sigma, slope): b = truncexpon.pdf(x, 1.0, 0.0, slope) s = norm.pdf(x, mu, sigma) return (1 - z) * b + z * s else: fn = mixture def cost(x, z, mu, sigma, slope): return -np.sum(np.log(fn(x, z, mu, sigma, slope))) benchmark(cost, x, *ARGS) @pytest.mark.parametrize("n", N) def test_nll_numba(benchmark, n): x = make_data(size=n) @nb.njit def cost(x, z, mu, sigma, slope): return -np.sum(np.log(mixture(x, z, mu, sigma, slope))) benchmark(cost, x, *ARGS) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("numba", [False, True]) @pytest.mark.parametrize("log", [False, True]) def test_minuit(benchmark, n, log, numba): x = make_data(size=n) if log: def cost(x, z, mu, sigma, slope): return -np.sum(log_mixture(x, z, mu, sigma, slope)) else: def cost(x, z, mu, sigma, slope): return -np.sum(np.log(mixture(x, z, mu, sigma, slope))) if numba: cost = nb.njit(cost) cost(x, *ARGS) # jit warm-up m = Minuit(lambda *args: cost(x, *args), *ARGS) m.errordef = Minuit.LIKELIHOOD m.limits[0, 1] = (0, 1) m.limits[2, 3] = (0, 10) def run(): m.reset() return m.migrad() m = benchmark(run) assert m.valid # ignore slope assert_allclose(m.values[:-1], ARGS[:-1], atol=2 / n**0.5) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("log", [False, True]) def test_minuit_parallel_fastmath(benchmark, n, log): x = make_data(size=n) if log: @nb.njit(parallel=True, fastmath=True) def cost(x, z, mu, sigma, slope): return -np.sum(log_mixture(x, z, mu, sigma, slope)) else: @nb.njit(parallel=True, fastmath=True) def cost(x, z, mu, sigma, slope): p = mixture(x, z, mu, sigma, slope) return -np.sum(np.log(p)) cost(x, *ARGS) # jit warm-up m = Minuit(lambda *args: cost(x, *args), *ARGS) m.errordef = Minuit.LIKELIHOOD m.limits[0, 1] = (0, 1) m.limits[2, 3] = (0, 10) def run(): m.reset() return m.migrad() m = benchmark(run) assert m.valid # ignore slope assert_allclose(m.values[:-1], ARGS[:-1], atol=2 / n**0.5) @pytest.mark.parametrize("n", N) def test_minuit_cfunc(benchmark, n): x = make_data(size=n) @nb.cfunc(nb.double(nb.uintc, nb.types.CPointer(nb.double))) def cost(n, par): z, mu, sigma, slope = nb.carray(par, (n,)) return -np.sum(np.log(mixture(x, z, mu, sigma, slope))) m = Minuit(cost, *ARGS) m.errordef = Minuit.LIKELIHOOD m.limits[0, 1] = (0, 1) m.limits[2, 3] = (0, 10) def run(): m.reset() return m.migrad() m = benchmark(run) assert m.valid # ignore slope assert_allclose(m.values[:-1], ARGS[:-1], atol=2 / n**0.5) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("log", [False, True]) def test_minuit_UnbinnedNLL(benchmark, n, log): x = make_data(size=n) cost = UnbinnedNLL(x, log_mixture if log else mixture, log=log) m = Minuit(cost, *ARGS) m.limits[0, 1] = (0, 1) m.limits[2, 3] = (0, 10) def run(): m.reset() return m.migrad() m = benchmark(run) assert m.valid # ignore slope assert_allclose(m.values[:-1], ARGS[:-1], atol=2 / n**0.5) @pytest.mark.parametrize("n", N) @pytest.mark.parametrize("BatchMode", [False, True]) @pytest.mark.parametrize("NumCPU", [0, nb.get_num_threads()]) def test_RooFit(benchmark, n, BatchMode, NumCPU): import ROOT as R x = R.RooRealVar("x", "x", 0, 1) z = R.RooRealVar("z", "z", 0.5, 0, 1) mu = R.RooRealVar("mu", "mu", 0.5, 0, 1) sigma = R.RooRealVar("sigma", "sigma", 0.1, 0, 10) slope = R.RooRealVar("slope", "slope", 1.0, 0, 10) pdf1 = R.RooGaussian("gauss", "gauss", x, mu, sigma) pdf2 = R.RooExponential("expon", "expon", x, slope) pdf = R.RooAddPdf("pdf", "pdf", [pdf1, pdf2], [z]) data = pdf.generate(x, n) def run(): mu.setVal(0.5) sigma.setVal(0.1) slope.setVal(1) z.setVal(0.5) args = [R.RooFit.PrintLevel(-1), R.RooFit.BatchMode(BatchMode)] if NumCPU: args.append(R.RooFit.NumCPU(NumCPU)) pdf.fitTo(data, *args) benchmark(run) assert_allclose(z.getVal(), 0.5, atol=5 / n**0.5) assert_allclose(mu.getVal(), 0.5, atol=5 / n**0.5) assert_allclose(sigma.getVal(), 0.1, atol=5 / n**0.5) iminuit-2.24.0/binder/postBuild0000644000000000000000000000004414332717401013336 0ustar00python -m pip install iminuit[test] iminuit-2.24.0/doc/_static/bench.svg0000644000000000000000000035013214332717401014204 0ustar00 iminuit-2.24.0/doc/_static/bench2d.svg0000644000000000000000000012125414332717401014433 0ustar00 iminuit-2.24.0/doc/_static/demo_output.png0000644000000000000000000021453514332717401015464 0ustar00PNG  IHDR|q iCCPICC ProfileHPS{C@zIPB +4EХkdb[b]EDY .0w߼;=ߜ?s a )tQ =2*X@$ 9wi}x:ֿ_EK"M | Y(AĮ:]8ˋYF`WlQ<$:bgp8$gM\a!Ž))\# 6t|Fwq3V͎l/3w kߒ,ˡ,R+xgR}$,c>w^asIsc.G7w,ItVCX,'re1[4W&'X s㴤yW]$x]|dozBw|<s>fZ6.}'L/Lw&Jyɞ{ZFdo:A a";ppCaX3`[>݈kpNg"7Gg 8&L]=hy[l::o@k$ļMO sBX1kK@2HJ@ h}`f 3R7 R r. pԂchg%pw@7xx0 A"CTH Rt #b@; CQP  1BP)T A@gKu z @#[3I< ‹ā}Px93,x'\ WG&|W8 P4@PѨ8*BUP=T?j ESt1CsЫyRt- ݎ@aDb11٘"L54 3biX= MĮac]A8Sp86.+] >xs>/o A`G p k ÄVma(K#:C-bb 1񝔔T_jTqkRRHr$C+iILI!]$= #ɺdgr49\GL~J(M6fIs7II7Iߕ~-Cёaʬɔ)9)s[fBR\)lFJ 2.K5 M͓="{]NNW]+%wH EբR9m+!yQ>W|B2s 4MƢ%i'h= T0Xа ŅΊ<FnJt%w$JJOʆAʫ(_Q](~!ga ***TRWUST^VU9%WQ; /+Йdz1>!֨Ԙ ܪ٨DЊ*jV^]PI٧ӡ3]YK/S^>YI~} `C0ldm7oԵv`Qբ^c18øxfkդbыw/X4#393offo 9e-,Z,XY,XYQ[Y}Y7Xhؔ2<5[&۳Neold%% :h:*14NUNϜLf"(󵋩˄׋n(7ONw90RcV_zNh;8pJTi&imXsBsKTK3m5ٲs gyaKm+]|=ϕkW=^`v\pugn0n4ߴt߬~;itv;]Ku{۽Yovww.xÌ6?_"džވL{R>LN|TXs_p_|mTԔ-bό(dqqĥ@f'g:DM]~쟞A6@A8YwfNQYv !3wuS騖_G "VeXIfMM*iDASCIIScreenshotXOЦiTXtXML:com.adobe.xmp 785 640 Screenshot ÂK@IDATxHtw+]R*((A@ADQT TDSw`{=9|ٙٙ3CB ]E@PE@P$@$SS"("(A@ "("$1&*"("PE@PE@PJXkuE@PE@P"("(I %I"("(w@e'O.mڴڵk|RժUM38> `ؼ+믿мE@P|}"p#>}zi߾\|Y-Z$ϟP^xARJ%˗/M6Iڴi_q/_>I:uK=WE@Pb%1 fGK[n!K/$h1" \QΝ;gh۶o2fi׮4iDfϞ--ZK%wK޼yM>۶m>}ӧu)瞓r  pQE@Pk}v 7JLB Rϟ_re̾@`=}JesNܹdϞ]0"lٲw!v2f dZrl߾]J.-ow 7ȁdR^=ə3GE@Pk^ JxWpaG:tK. W͛Kt̢4|HVgxh;Yb@>-"2eȪUw2ߜ"(jݶ՚%@ϟoH _4ou YXGɓ'n7/^(3f̐uʛo)-Z<+W`ȧ9q~Xr {GE@Pk^ J;V4h`V /=\>uꔰ:$M4B݂x&:|*@np"(j$plƮ$$TZCˢ("(@ 0@G("("PZCˢ("(@ 0@G("("PZCˢ("(@ 0@G("("PZCˢ("(@ pp]lY)Ydsz“%K&ժU:uȹs xݿeݺu~zO8'2dM.݋ J*U2o\OxtNx~>}p¦sL*$O\~GꫯLҥ~ \!D>Y=|u A K!#&`::G%zh>SyiӦ 3U[Р+X9W!&MN:?H筷y3|7|cMw!͒%q=zwy#̙3ڵkĒ:\Vr \Vnfc"޵k=P@}C Wz ¬r7+tT ZbŊ{n Ô矍b^L֏4$oŊ&AȒ%K̑|1u[NOb=QxCBI{K.<7ʶm-ۅG #_~E'ٳg7XH~%X/^ln6CasҾ+hy6_xS7?uO/E i #oz߼wyJp>th tVpɞPPcؽ꫞8tj6 HuǦӣ"#cǎ5'Q˗?pʔ)v]wry?O`KAԩ!VE4&%ƍ?ӓQFb,*"4t &US '"#h&N(t` &_#jZha!Ǐd,?dʔK/?Dv uf3NQE Ẃ%r!w…Y <&3Q+1&,T l|oQk!tv̡C_QyU4lP97NLcٳg^YjtU}z#̙3Mt%g=cL=!1GP ,ZhQHHYV~}3dիW7;DB1 ^X [zIeg?Xt(@A Wk +iY:tt#G4ׁ!tX=?ٙYrb&j9V2|1۷Ybgh qv78a[(@, VٳMʊjĈG]cJg#+7AXlW L$2 +@C Hd\xq9ZhdW|&ā;?"2ؾZĉ:sE !>.*XXf&>}4vZIk8"(".JEJAE@PE@PZ E@PE@P %q>HPE@PZ E@PE@P %q>HPE@PZ E@PE@P A8k}"("(qjn}"("(o-"("(q8[("(@#0@K("(@"0Nև)"("(6("("(Sa"("? h*T%KF~Y32e9-*"(@۴is/bw +7۷D;w9GOn|  φDA2G!2dv͓b {W_}С߿']v5A uh!O?W;.1@VYzw /{56lBS}Y@b5jX-'ao5PPE@EbF|I`b D+h6mMTL 4g?X C3Onvs iaBFef4Tcƌ!~p-j\ ):FoRJߨ|1/YD>7(PmC &M2Ag͚eSJ%ժUэi܄ -}knܷγf*`3IL ^۷6@InI>͜9hoyUE@PF e\?>m~'g͉1m}+h jNA%Hh}Qw4獁|^s"d]Knf)]!%<Ѩխ[&_|EL:B t0~x8֭3~M>|;95+W6q liu[4hOⓈ&k ^={h)"76=7{yg|ƬG ٳgAc68OԫW wODPE@Ebb.ocYbٲeaNۖ͛W+hp:Eh]$~o4*B={xںuZ@$0B }5i*a…f biXa!- H|$ׯI/&Dܽ۴i1#Lv!MZJ%ZAG?a€-sɩ?l!BzrUVPE@P!Lsq !Z|Pgx: $G0P@hwlL,&QԩnO\"~65բ%c[zA1b~u < n澆'0ķZCySG0w ,:ᘼlbzB@&Bl@!<@(?]?!.j^-6mb0p⸟}c "("d- VK03UjժFĢe ;mT8A(nmoB%/E5p&6uX|A3g fgHVxYj/:Lf9L]͛7k{LV7rȀYC{Yr*4hBix]̂}gISYU^;āپ{ARFH.Œ:x|iOK( }Ѧq2$ 2Iս"X/pR4,^ *i x -#za8}!1!Cؚ& _͖3#osgL7&.]Yt!G pzx cn|9("6&+߫h V+?rȢ|܋?|Iڏ?hVeF8-_>R$Vb86cԩg [+u>FC o`F/9dS$ {Ց%C6<"lמh:6fի6(ͯa%.+?7x㙘.?cǎ5[␧o&lÉ}YjH("0@b< 6;E!: M OVB- 7-8o+WxhbrWTA)/`CE}%PotO^K}yoӆm8ql~[" cTE@PF@ `\#@ !f'&R^Bs{hUE@PF '>/<6>/Ɗ hSUE5("0&ԼE@PE@H04QPE@PD@ `Ly)"("PI("(@L"0&ԼE@PE@H75N"*"( mz$2i6LWE@P$$ZeE@PE@H(LWE@P$$ZeE@PE@H(LWE@P$$ZeE@PE@HȒ%k ݮ$v%Y I"˔!Bݷ,G=(Yrgp0gTY6{\8w^t#/\ey;yzqPB2\nON;!SImqrp~ɖ7gXt'z5g9qڻ_'.^t3Hd. ;Q#l_I8sP%/]6ߣGҤKk|i.GVI*nW|gKl&x=쩳DqbƉdsJ$s.ūe[Hl5ɤcZ7-]+? J*50]ɞM;$nk}dp Y2y')_j>|DHDBH'O.~!_\$8vk&%gW"ߝV#0Czu9F/aΓOel#{q9l٭TkqY2sOroRuy2C*6.>So?鹿u`u\\8JQ㇎< `+.xG8͘;?ŏ&> >m x^}7KiȝrkL57>e3+J&5m2=*%!cc]χ%mtĄ##ҤG&I!ws5'gF6y$H^qd˗L8_.̝0MnY,ɛS-ot5uڱf=BN{o_8}$s?so^Ad|/辟W:-vbSĽ񏔭]YzeiJ&G7w>ruH;VMfrЍ'Fh5!տeY|)P-V귥Ǥr`>[zixo )Zܿx!70e률'[ߤ郷{c=nOvl%%59ف; . iǨ!@ΡܷcW&4WVY,2*J{n˟;}Vf"Zen,iҧESE]̽CA)KNe0Tj*3WFy$m RbIO~g߾f f[ßFE c.WVipws)]k֓9ηu}BraN_2I!1!mģE9oeϜQ*tkT%\d!GiǛ;FM/w 2G31\6k|3)V*Wƽ@&g=Ƞ^uc2Nr҈\vQcⓆ2'#=|ڜD HEM鐞^zߘl{dr1ʹ?Ie߯?ԨWqL#hy\hg[O.M(@ ss3$fz:,L $>3A*7v`׿RY-9 {!Qĵ7?3 ģ_&s&Lru*RcF$Yd?C4Ay3_ms+Vp/yMM̙c!WL ~ta<限]pZw4k>{?qFfCw~!v[O7n]X,5SGO- Qe3 xğJӚ2!o9}3J݉;C@'*W̓$M]07y:O(j{ 5[Ƭ=5du<1 A1h9`Sy6~dPRgD0hTu:P S۩Ʊs:]`, : ZdrFA\%? "p#V<orDj4U2 ݠrMZ0~{ [d?~h޸;0FVVg2S~5C8b6?!$Ww ߎ[uEL젼kv;ڹO$wg,SJ(&;1&Nop%& "ԈWw Mf>?^Lx]Յ?fg-2$׎`0]I R|!Fnq",̎ε[͑6Tace@+q3РZޢ˅kѐ?HB*4t4w-%܇[ V{= 6ZL+^!|3yk: & hx#+iҥ1IΟ;ӷiqȰ"PFa֨ժ1#8HĀhɐ'B$8Xom"\h;ʇAbI2@3˘_ nd ̩+J*[e18똑}!4@N_ɫ\ xAИmp9 keLgL~o D/Q+ĦAӥѽ#hËH孍n}YL7r`<:9V,h譕OsܚWɯoi$+/^2Ao|{ ȥ9o<{{{p%Ef9c} ޳u1#gVg N^M&mZ΢A.ž87v0G=$DE߾܇pylGC-WW1d랣SCұs[S{×+( 3< lTLhϖm+2}Em\|_jRWxV[& ]<+<^M;JP},t|ɀڷ}}e9sg솿*o#U T%p>zf=u:B)z!w. Zi1RoY:f.7O BQ; F_--2O׭*UղAzA`M>! d]TiRKA:T2Rh䄇@%w|GM9#߼ToQGgΐ O%_(?_z &T DOs%KI\q,XY )*5n+%kq4Zq1uHBMR=i0Lwkh#!U(5ooh\भqk=3nYL{ٲlHRJT/+-n-)S2Fx_*5!^'BeO'h~CZyYtUfqK=)[E' Zʚ~++,cJZ=7{Jih̒%O.f%7=R5-Xk,]DZtSrc Vיf{U! 5"5b\F2ri|MXU^Z=ud˛ePBƾ _MrƬ#~v4qկ#|3||dEZhrEM6}ZG/hP+I6Y$?lHrcL|Ҧ) ?q?{N(qrH*rO.~G^* DhA|d`wI1}#|- yM9~nXN=!Cqw49~G;C0v!CrϛkaYp!K?AY6k|3CHJV/O@C6%ū_>^΀ ႠtHiӎˁ佇{ u$X] /.yrP.48:Ab*"zU2d(u4,H>mt湄Ѡ vq8G8`^[{[-ć~cCpȒ;!z)RSuoKM2hSyxmYQ:|ŋ]%MjeL_0g4`_|gMvJnhY őϽkme)b]I; WHX$z sC*t'.!78h73 eu4ȅs-"#Fnp\;NA{fdAY3˜.Sz~pW0Z;f|7h,ԡ +SkC(Ū6aeg>!FK)D{Y^5ٺr)8`OZ8/S.6Ni}C9~f(  A+`۽,PL'/-9.VZbIdF+0"/O9a%&v۱Wzgf g+@IDAT?i?࿇ƯevcV[>+VūҤC $% ]ֈg@Dqb(T^W_/QȘv+8>,Ÿ7I&g>y#}&c"|ZW]bV-GyQB^D :ĦM+_lЬȝ,`f=kA0F2&}Ё㿇鉸 &A: fXV#:By ~b8ddܛ#+ׯpf (/,bE i͢/^ p}""2d"Zժl`ՠl|J1~b-[F{d!48>|Loklt`v-8[W>i,`/S4ŦSgqL" 8ۏ zY̿M +ۿ' J9$lv.le w{4$e׻.}Lޤ!lrzeO©+/&$2"̾x$wX |Ӹjצ']g5>2boNY%훧^'lǼݤ>h dHož#AKe Ӱ 2ɶܮ)ۣ hs|e.w;u̿; T&lXHnzb-=FhmծӓƉ^# ă@B yrMxjbdAG0`J~i;fG{9Ӳ%#CW 9|bRN?嘴S4C%m:iÃm_,N]R1|2.\0{p֚]E Է[l4i?xG.y}h)CT߲յ~GئGzWc08CZyCܫ8if&<Ú|g'292 ;+;ߘ%R9yZΜ8eVyϡj^@A e)TP)6,Z-)uIn^I98|Lxk'&,>*GIKϗ"J&j%:&&;nUH6M$O={Y\Gv@ ."-:)9 1w&~ټKr\KBJP6TޞWOGM!lysHzU }y猟*E?JsJԩ̝Y3upDK[K7k6K2EudrrE eO=(wɘ}lq)RJ[{9R~5Gμ] +.-5ybǡd#-7ϖRq y[C.gVOl]Q^m*%Dz귥Ǥr`> m!E+D;_LٰpdvTjTCjn=vʤotٽq0d<Ӓ.Sz[΂y$P?,c߸bV_sRO.OjRO֮)(@#&49ns}Ø ZtyS;dOη`٢Ҵr`>yry9\,֒!sFMMPy؁#27e_+u$KN'>B\L|w!EEtQClkDݐ~ ezDsïagBG_QY-3CjPO;ot?' xJ?܂+mtI䗿d!s:mSa6D|T?!/E@=T{{^▟?Fխ"?#ɒ%Zws{lVQG@ `c4ʍn;0و'@k^^JGg5t{}3P2ߐG( >O:K_5,O_lGW`yGKDO&`rӍ}V@9|͂ >~H4N{c-<.!wxg5Pa5ACOp@o%cr%s=|zMZŤhR&l7y8(o/n回볧#n|6阭s_z S!_VpT fEE*ۺyΉ#=8gɕMnrؖp)0Wn e׻CyՓB@ پ9BWc4p4fwo{Xs'(>`'E7t{wok`HVǴ ɳe.#, At[auw};Jq3Isy8?bQߏVZh4,v4Ȣ,u<2YtQcF +& ~# ne\JBݷm{L_Cw)Tl.8m`zkٵz&6E fE 1co_ hSe杲!o/5yIoX\mWs++,7u?mZ͔-c2BBm"~4,2g1-w|X h3GyƬYuCSJ>tC -fǔloe+"nB:&a$s+&Pl滲9Du$aڨ/''\4%L̸L;+G2̫*U2? bb[_'/}V"3W7 >& \ c%_+K ~zYJKƽ9"أ"DFp5 wWS9upq$'Nn©>Ks F ]9?M;2ʛ8nA3? |٘+9ڕv=vGs\KU,fQQ!6Z^C=Q֮,g;ltI~C[>~y=kGhRE _\%vQ[ q8Wyme|O?1KP"mg1}d}s0Gؠ Ą_Uڿ9E@*T貿_1~;6k~u?<靭^|5~@H'8qV4+1%"`@_Z_ /QȃgtK| з<.YԂ֚y}LS'}9IK|q_* _.8ɸw 㧜w|` Zk %XjuE@PJ^$y=:ZE@PE@PJ@#kE@PE@p#Ѝ+"("PY("(%n4\PE@P$@UIZEE@PE@P$LͮVE@PZwE@PE@H(LͮVE@PZwE@PE@H(LͮVE@PZwE@PE@H(LͮVE@P@,Yȝ;hB'O*~bŤaÆn:|7o^i޼&O\NI:A /.3f/>nz;wN>Uٳ-"SNy݋ [eˊСC]H?~_߬Y3)]lܸQҥK'\_Ey1QFRZm5kVٹsgL<"A]˗//6l[ HӦM̙3rԩׯUF?뮻NVXNtqR M7xo^4i"]v*m˖-~{'拴iӚϟ_ Uz{ƍ˗{ȗ/0 &DI>do^E,Qw3y݌ [nŠشi[ :~_.]D(kB3?HBRL)`_#߉ݻWϟx S={ҥK#駟"Ea֭ڇvP8:Ho_#F̙3믿UoիWUI"W]mm8eWfw8g,gL$Jh++W.y衇>ANرcr 7D˗/ˏ?0#$gΜR`AٱcWxs}%sX]!gϞ/:zKz9 $jHzԴy3*QAc alI8z-;VZ& As~P>b믿ĉ=c~h0!REݻw7$ ;W ?޽ܮY`."-r7+dرceɒ%^|*4J0)6LVZe&:ޘ,-[&6b}_}UI,!M駆0.^ؼ{wig\0{3pN<ٔTRK/ɴiӌjXXXda壏>yɘ1c]vlUV=a2zh4ݺu*U! W\io]6D $nچc6}XpaYС=[n-v1h|qc6?yq qkB?7yx'LS} {w7{l /Zׯim8fqAcceGC;ӟcbDl?CzmªW.;~܈є1Ͷ5$B 9R2d`,=Ln0'oϝw6T]?,.!pq{M1z!SN5PccՓO>)|6a„N^ LW^|.Z-M@k%[l@*{iNV:uGI:^{͐?kr@b f6m$0馛Ě 2>%T Q9sAN2 0?:-[}g̽h_H TʻG.1P1B >>mԶm[3$Nl  鳂)S!tVԍxƻd_ƍNA1 MH_۠E[ߠlG5e%̊H_^bD YuO~"x[ C8 DhFYwǾL!-P;^D˴ Z^~ې &ݼswЦGia]xW9oH(Bş}m[CsF  Ӈ[W_X Ga2RxqQ O?SRdIa\/w8QkR}ff?tVkF&g1 鑞={Y1Z:@ff\R%cfš멧h>1BrʞBgh A2#D^Cx⋆/^b>`ʠ{'"PBDy!,oAhA ̴ܳ[ 4Qpq~G=n33DV Hdܭd'Ǐ7$}(3ߞwn5Bi#)A{СFλ }~ 7Ks"7߶a8ɷmѫ[ǼYƻvZ0h>$ .4m_o(s#0Emλc91I߾}dzMK(>2>m\|jŚmA4fhѰ3VWVۂ>>-?Q*1qǚX / 50$c#<(RWs-KjȮڄ!t.\0d˔)cފ]XAI0vZ3söiM$31P3dn-"ᘝcK03u  K|)fLLh[I&y|BVmRY?/Fc:._=k֍Aw}]]\ l` LADJe u' +)2Tt#wdwlZ߶ ۏ ۣlV|}@Ŧ_LXY9ןya#?7d2of;tLFx+~uqOL`cKA|- \L L X# ML^h(@YDajB$ݚ&w941ponTg&6,6t4L:\&" rZh'p5fH64|~.'q!V-;v=06ra6D̀w]©o(Pj|w}f\jm{I?S|ݲO sOjt~s l[::c:Z 5 >n,yyDxM@:kc1{X lCg=@V`" H~(A;hD2y5q$9b/_o,vhxC9XHL5As iX (c[go_~Pr /J1tقgȠ^Nʅg 3o YE}! dglKŅv`2sH 7Gw{@+hcDy ? =oZ()7_vu|qya`p@PY@o$w'#_1Mci]\1,aIap54w]);>W \hï6M0NBF/~DIi XLϚ V]^ tK8% ʐ+f4j.MwHy1 u\GS:p)o'pM sVpGbWI1-Mѝ \,=0#ªOn4~ b)kBq Mv4` .pMSG['΋*p )󎙤z; Նnbrk)w [/ /m;e0ZLn?(sé;~`ߐ;/{oķa @μ֙ Tfw6``O&Zvbmd`Ʊ6 ~2/6Ŗ%P.3,!-[^1>^""@9 !d\j]96j. <d mLϺѨ֙:!tt3+!Irak}MZSwb Tvߺ$k8Q b cޘ,%>mUPߐ'6ޝxfBϓE"> Q)ߵXPcĄ̓˥iE@PE@VHA_RE@PE 6Pjފ"("$@&F")"("(Mt5oE@PE@P J`hE@PE@M&"("( A"(g<6J ".RDX,XPWWlĶ kW@UQPE@t{_dd&IN? {@Hl:8 $9`0݃ @D @INL @@ ` !@@@&=@ HH{@  /_w>)`ɭ~v[ڵmŊv@ZZlҮ˗/ϱCeuYֶm[;mϞ=uL,Y;<+QFa۱cG4|@6suN=T[vڵ+05hifE5kdIsQGE]dG͛7g' OСC:c=%4# ~k֬>| .쾘w` K/dʕ3 ޽{;pJi</nO=IL/vE-[֞{9+]CرcURa91qZ.]ܿ ʼn/wm-ZtNrJ{}i8 lʔ) /⸀ #/AvoܹvM7ٽ|`m ,Bro/' d:u[ovm7pI@؟Dn߾nv'7md߬RJ.iZ5kc|7Zr|M'>o--X͙3Dž{y[w;C/ YV\v%ʦN.F*MӪeʔV^^jM6m+V̮:PJj|^oV0M/[,Sы-rSj[3vaÆ5? `&T*[8wqv%K8Ky|I߿#M뇅?믿+ @@n D#H-Q֭M 2/SdL8^{52yJ*es[w)imq7n\L[_jU7%oe nC֞>ngvlc h&Q"qB`}h3_*L2̙3' +O"%zSNÇ;Gߖ/_bp 'iii&8.>vin*Ocn:{`Tjղ}QGe[n[n%h}xXG}tq?=裾z=[%l]gyԩcHX~'6l0_9\@H+6/nݺٖ-[ ]CI)$$fϞxw:ֳgϠ '{5_~q^&M<:I-\y ]5jUV.BF~X -[։_2ya[9yY޽]}I7hڴiK'svu٫jKۻ4ZrO"ku;v8. dhK jP毿|/oqƾ:_y}0//<8&DT^졇wy)NuȓYT)_SS;BӇwM#34@=]wѽ׫WϽ4#Fp6N>da曝W6yH_{5ӏ MÊq_P`0tc#)wJXnzXaW=u7o>oWiӦJAG #D=QPEmh5kl„ nZRN*Gml$ 5YR%1c>L_̚Δi`mnM[oՖ,Y^ws+yWO?䄀fx㍊7SZ?g_:O<]HhJ$*d!8q[g&A-V籒 Ijٿ^֭zqLSqwo\LM^C}NeʫϤ~Lx&-atR8O5rBrÆ ^6!H+@Dš5k;̇~h"ӔY`4]&$A}Q[oZ%ϥWIɣ7n8{饗\2y|j׮{رcMS2}A+[o#Cdȿ^ |]DLd!קO?b*, L?BL(4}[j,TRFSRmݺis6+襩K=o;soI}9~%?zi+^}ID"0д[ o= "Hq\)a:2CS"|-P7}Jv ZG'0^5<^_^۶m"2)Ee)F?igyhMc57nt鵉AMy?~hW^AO޵6Ŕ'GQ\Cp"[X!ԗ'!юImZMivʴ.ʒ.oS׮]fmNիWīqCyڶm{+EkNv#_oXґ#I8ŧn2L8y/^޵Tv5X ̸0״G(wݴiSR-iY@ "HN|!p@y>jHN^yU䑑{XڪWHUXѮj3g۷ϕ;-Ɗ7գ+VO<{ѼCbJ`Lq\evo(~=z-XI}ᅫѦ_ڵ/.Ӻ*LkUuer @))ĪPBnc60X17#` گ3f%4I D`` 9s%;yp !BrG`  $,`  @#7rA %Lء @ wF.@  ;t4 0w@@sG1hxɒ% /uرc-϶O?~g?~9r$SKFm;s줓N۷_|aWTnjԨa]t-[f̘a&LȒu֬Y3ۻwM4~,i{ڴiwٙg> +_j{6y$B*Vh4 [L'XmnUSz6y"e+WThUVu~LQk֬qdF+ f38Õ6u]{XYuJX}~gvh YQJH0kVZ2֬Y~2% Ory@*L3yci{qՕ*U*SjK^%صkKƻW3ZiT^wuiDS,Ӵ^|3gta.Z?M1…yYbQ^{ =Lg%{aG2@@Tĝ07eK,qE{ө^='$$T)JGVuVݺٳg6qhcyОğ}7N6lP>q%7x[j/1A HiWX)AbM `$n]F]dmڴ-q'LO%<;Q0؍VС'P޼ۻjTMGkCg475pmR;\zm>M 51yūw@@ %?ɮj;묳g`'u ?IpSN9}QWSй{:Y]ZثW/ZFjVs:/hZW;O%\uVk~mFЙb9n8Fk~7UK/u@ol ޣG7⭃fH@Gq0?yL[nn´~_^h>MKh%QÑ׭[n`f ɓM mx.bXhq#"Q:՚'xµES:MzlaտK'y,ul8p SN@OS>}س>v?S!ώ ^  H'h# T'(aoT5yeE+ڦc[4꒗Pm<#ZfhHh+>#O!@ :Hx@F=0<) @*H5(huᤗX8p*T>lz͟?}Q_%θxǬbŊvAꫯ|QsqEuq-[;/lƍ[Tof/ @@ɓkEQO [N;Νk?M4ɉܗ:gٲe[n۷[NpӦM֡C n޽{w+KhРyeK{7:kwwX…{n4.]xMQy7n  P,ٳ;SOY \_;82d5i׷믿 x eo_{5ٯ_?FM?aÆMBAehRrO`vUWYV\!ʗ@Ӹi:TIck.7-;ϝS^"QS}+Vt0`^z饮pxu,[p\ +) 0Mj4HsA @$NjӧOuO?/Xʕ+%45eZ_VjU}J/ɶmۺ3g/qAM͘1 :˭RzmϞ= %JpS,}.(4^^y4={A'N>7tUV.Bkذ-[̗N"z{. @E kkժe[l>hM_|좋.r|ӗo.]:Sr-|<%9{l>||6j(dСZ=j9Kɑ7FpGGg}-^@>}LAo@I-ЦmB @B o߾v%y禂5kr?3"B( 4 Zׁt#\VM"bݺuuV h|R[ih۪U >'Cbm͚5Y 'eB_ϴ>uǎ64o:FkxfQ? [K=~ZGy׺~@@<@Bsqc/NL_FijQ[/FyIri}]wnJ9WGiӦfZi3 e具qBk9=ӏ yy p{^?ߗ]71Y8iQN2QG"ySWրꇅcIϤ!hv;U+ '>߁Ohg:+Q$a#odC@uL`KUmMZ/aڡ)7eQ9WK/d><),{Ҋֵ=n'ƚ2-%iΝ]z >;kX9W!Z'vh^Ig%LV},Y\C@ڱX|e-_(ӓ})J(t谦ɋ6yal)34c8=βӱ+zK$#9ϫ٥Ӝ9_~ N,IdH @Q'5NtğvVT}'z8.0ы įވ.q{we˖K!@F@O#۶m͝;7Xt҇J3k .t=3g?> ϔ^v?%M83=Y"---䤁 @! رVZ2>#"#Fkn;СC__n|5jLg%K/>ܗ&͛dz00b @ ;_=$I=z-[ֺwnuqp/nuSL !C8𫯾ڥkժzꩦN:0@ Ė@ě@J*eժU?53p0lСVjUSu5a @C bغuk[hM0>]{BP7ׯN;͗ƻ:Aa @C )`M5ʺvjvt短f͚^o֦M߽w4'Nn+wޙ·lْ@ X;ziE?m}S~ҥ۷ϮZ_/."wt{L:8_b. @bF 5:9Xbn[VZ/]wkxznߴ6Pv駻0a @C bfj?6y*T=Wz >~GNqޓCq@ ;j^iӦa&A x `LZE% @Foh) '`|  @Foh) '`|  @Foh)O@OС{H' 09Ƒ^@ |@C:uXݺu̔@ ȳ۽{x㍙vZkѢE0ݜp 8  @`D<L 5jX˖-3E[ڶm)L7~7Y½'Z]h!^{Woa%JM¸ $'8c ?~=YzKKK0eg:upB4hP0n #M]}T믿nz).ZLcp_N֭ծ];R '(쮻BJ*z, S\('1ЛW_!C#Q&п,Ͽ rʙDWA[~\-͂!DB OknM4ZjeS"O@[re00=@,_ܝՙS=z0 @ 1 Zxe˖wƝ={M:5Sŋ-p7Sn #u;w̱~˝0aBH@I S=+lֹsg7EGjժ0@ Hkx%E:@G Wp͚5쿋.(d*U~"Y]0@ !+c_rɫ4j&MT0=V@ bzcPz,dC@-E@qVPt Er Ğ@cjomJ ْҥK` ;vB@O3gj*K SOɂ@ EPEc!zdy/|~.Lq $6`b@^|A@r@&8 @ 6`بH@09Ƒ^@ FEB@ r$:bM@l% cxc !CݻS} P>4 Ė0  8`  @%-oj @NXC@ @@l c˛ @@@@ [6@ P>4 Ė0  8`  @%-oj @NXC@ @@l c˛ @@@@ [6@ P>4 W_e۷͜9w dO=b!8#Э[7kӦ9ײ7xÚ5kf}q@ C!3{laȄD@Hq}$ M馛l۶m- @@ ;B׳cF @@ܐa$&˗pm @ 4`h6@qDN:a&aJB@IDDIW 6mv"Iv$ DI4tE ==ݼW~]vָqcKKK˶/""!@y&sY…B y.+~m+]t$jղ!C'~'k#_JrO9 \x=w\kٲU\ٽt0 @nȳ7gkժU͞=ʗ/%@LH:vhSO=-Zd?S7nۛdׯ+V̆ ƏosL@6xG}d\+VXڵN@ Ϝs8o6m۶Y`1y>bÆ Mqwyg<@G믿Fe(l0 G"5o'BE"jժ>t^n]`(hC 4i$ZDs @ "^믿Z ˖-kguM4)S{֮]k-ZN8A  U^zv5X֭rbݺu ߅ e'N*Udzu!Tr! g0MZ;ydw3{`h" ">r ]wrggmO?3g}6S3d H 3gδK/V^k5k[no[oŹ}>2\@ `jլhѢ>f6kM{tݩ0Ņ2y򷯾 k$y9۵kWX(2k,o J@@C )իl}qJ7uT߽w5ٕH|ܹ4s+WY`A@Xr-e{w|-1c[W~}_i?-Y/^l %0|p^1nܸ좉 (x .F_ot:YGٳ:uk{ 6X%ܗQ,y!Ki/@#X}v]K=+3t=zS^a<eʔ.y XM 0@ 虽:IkBYB  x `@@4svOV|~NC'+[@ /yG^@ [zx׮]Ox#A@Q%*N @f4w6gƬ'FGxƍ;nZb0GA c 5m۶}vS8@'=sj@(Y{ҥS 0  Đ0  \ @|(ݦ/lWoRŊQ%ZWr% w{* T @  mDhÿak=Xxf\՚֮2m4#};Y$eAHhL'x,;ӣ,v 9~$ܼۮ~i:'[>U0 @ ,xD"@W ͉`M&,Xoz]ҴuU*S<7Ő"$!-bٺ7j(>&.ܐ!kF\  ΅P@ ~|8:}TşW3?`!@  *EB :2߻6iF6o| *`<@6o_ަ/f'۶uMy/ @ `$@5[U'vxA1{ב2ߘb篏YT@ U Se'"$ wKZ'œK~(|ۆdf^@Cd!#cCƍ~;sތoL/Yivp,e%3mƊy) GK@Lgi'k5>dFV4m6V ' +#8yo<@ᶕt0F6B Fҏ;ߚjWm˱ƵM~`{W @ȻMv@ .!.@LS!Y?4E/ߴ!P1@`IC9՟<{GЎQ_aܰI"@N H)K7>vE2dүv?ٮ]裏$ c0:CGVf<-?PQ=߅@.]ڽ򥒀BW^mv{=: [@C`-@:[ÎK>)0(}uC%BcTXڠAL/ @"@&vD".)Yb%Ҳ[5r'c @@FƋHSBK[BE%k5jSV؄_g[6 0JQ$|rܹmܼٕ3Hm?'>i%k6ʔWJb5:>)i_HrXiqdjdS$YH @ e Svx3ldiHظ=7Q<0|S:x)`LqS mlժUv$Y[]f_WcogO~ѯ!$.`-@&y}ʅϝ;^S̙33 }Jv{ 6]Gـ7@IHJR'l{~ؾ}j׮m- xItIx^LmiҤ{nm=[nv9D R0F&y.R7s6g%,|d'3Wn5=$;hq8;w l޼yuAH6dQr*t۝H*ٳ RX}rgs}L| /6gE}+ @ > E$Yf,o&joN݋dM/T*g9\CYΧ5[[,o s0@F ="s%jӦMZ~gZZK @t)9~M[yF@lş8ҹ䭢ɽmڨ=O6 eQR)SrU@pZvL\~{| @a@t4m ϟ|K-wqޕ9(7t+-p7w/́7@bȕL1| :_wuHĉl0_ln :udZkCHXnN뗄?qOFlf̘a $#RGuE.]ﶁڨQ.sqD;LaY~/3L>ݺw)@ 3֭[v^:wx eNwūxqx;=-_k4h` ,]r_ |N"R@_y*'W/ȴ3Q_F0Ņ2 J6md7a~y5?7P|]t5oY[%J0ذaCSD[l?>nUQϳ╏M߼i=6sm֪͉!Z @ "wy{y>t,Ax뭙wg=Gs/D{\rQAc?+׵Mްmk*TڷⒼhLW5 |饗lEBmmΟ{[w<.%N'!!"ڙg^vԶgX>o2Z=1kLu?#V>Cgxz^٬5A o+?m}΋P6 $,@T hxahe"K8c&LtC=dC '+i ` @w&嚭{YcJnݬM66vX_lԣm/}`0!FB 8׿[<а gVN޿sҞygP $`"mvŜ$Wﴱs Wg# b3g Z0G6A / La%yy\uȑ#MJ:@N s 4 ;vb!g((Ka Kt@<@ !w~Xv2)áiӦA!#I" ĈD+3+"TVʥSyYPN,a#<8* 9ؽ?X!KFmʼWV%vm :ڵkm:"!`<m ]{m7ٮի۰aB@  qTh P`۞ۜ9sW^VL_\ɒ%wpT.  `§j@ *0yϟ MDK b0F,X?#6e_5@(@O"IN<6cv򀏬@$MKX#i~|ZY$N:0@"0Vy ʄ%yMxgo>:t^:7M$ \ktd@llܹ>*ꕕ$e&S+Zc+$S  GqN@}K'w2L5C6 @` 8ᇐ$3H-r:z@ @@&ۈҟ"0zڠg%U'3Gؐq i @ #G Y=mO>ߞyt>tO+j!WJ@tv*ﰹs^_=; !ǂEFVfSA>y?ؠBHDD5ڜ1?YzzVҤI;ץOO$9iG ;n3|T d"!zE_ mf={̱+䘎O`]6yɦ Ĉ0F3gΝ=w_l0G`0YF~$ &VlORz5wsOW+TL^ m{*7ްFסCC"@$XҦ.dhݺuA)^|ظ"Sk9TˬҹdKԛM m?h~ spעVV\e_\KC8>, Y O9u ϻN"\f 6m{ğ:{# z5R/B6`Rr صkSkOeһ9[&cg;~&t`Y =9E cG\lqw@B@&0T$P,cp‰j%kvӋW~;z@@ݐ NYJf?ӊgutD>dkiihc4v@@,<իM/ @K5m e%k5Sq>/P&df AHTy~e:ӧOZ~gZZKב<!K@ %CI+\6>M":~~OB[ IZh¹sZ6mL2xb[jl҅)@*83Xbvw'WS6hcH(vXA_~;'\i0 \ ={ow쫯O<1nݺ8 07mڅHςSV$ji7 r%|Ak޼]s5!ѭ[Κ6m%N0Ņ[k۶m&ޏDbjgSk%1@DH}>.۬k׮5=4-Zds $+E?#Y9$R35YS_]o4)<˗۽kcƌq&M0̮, @D@m޽=#n]_v.]vؑ%ʴiz饗B%' KL d3NBhҤ9ҴF~5j 2d  $_wAm޼9/: W^UV zT\(k֬Y dB@B8v징 K6<˗#0ȕ<]vO'IOO;w=c[k@ " rZ5Њ2=@IDATz<XJ>}z`2 ζ }_*Iľ˓졋f|=Y"/;۽{)]gG HD4,qw$:]} Lb׬Y㎊tTT&>;Gn?i8mMm" 7n\H@ /"FRNn-jSLK.$فOZ$*wۏK7%jiwѳZ? ܏␑~9y r @ W"AUpS[l/A T~R=3Us@ȳP/=~')SW#I L[Ŗowv}n7T"KCE@ *JK()@YkMSXH[RzMH$ <@Y!),4 SW|2y~]7~A䟖k@y%F~@ $wڌ[Cecw矟HN:ٌ3LjW 7`~|an`+cۦxĉ;w^I9۷wawt_&. D00) XU O?{ܜ'Z'#޴ 0 ! شy,9 h/ZZe|,Y; &,#5kfc7~p^-Ͷw4=r /mλ*?f+K7 "1N@HG~ LBpǏXT9+-9iF[uo@hhj5_!wf N?6~3Ȫl=XL ~vRuSGL]a.81Eg %0qǎ!Yf~=u@`O,cRa#0!F&HQ#q>??GER  0!)֭[XcNK!z$4`B7'|e,̱Yūc$n0y1@MX#@IE{{f2ab%옌B#5xSo10G6%,5ҹ BE[3&~\fzz "1L@S{sakuh2'V>ty`{D&m#?'X !0o 2{|`ծnū˞w_\e_z.RSVn9 q1 4" h߻S{!0.86C4@~@YM)3}\%J|0/Ց#Gw־}{. ع}˺`!yIo|bE-mfM41yY6lǢE[n֦MS@07Ma+ W"%铎@NlΜ9!5a„6DFDd#0@"iK3gδ9MSJ+زM[!'$4' "!jҥKmժ?*W5k,h۷o+Wڵkb#G )M6uߛI?  B '/.fPy  )CiJJ*eMC+R= @:XF\]s:1tPwcYzf͚CXzu|@h׮]5שSǥ}/鰙02篷;y>pdH @0X%SLqm۶EO4Znmӟ|aн0y.9J;{6[|TUOBB!$!tQC*MtaEPu.t]vWpd&]]zg$BH!T҇;7ɔfLf~{Ͻr-^spIUY;EQ@\"@vy;h"`F;v'p˖-sp+[]'̛7J^@2L4`@,?%#{]饻o:55n;D~Z @vl8OMxR~ղ'W.]Toуy ϋJ+h |ȹRJJ;׹׮A@!`^¯M6Q)::DwO`Ԥ}bZ`:Ac={8lj$珝@xx0pOp @} xkԜ"_n"ڦs w%B Pu\oL$'(8ʼnD{v&d$g wSW/Pnn0ihĉA44@ .w[v.J3Ɩ]ݺuk:t9  *8az̽/CF gJ?~߂6lzvP1@cX&+%Mt(VMsP~ ;C e@@KJϒڼyZjAN0(t1/Ţrz 5(0>98p#*8haD_R4;{yYל=E%Cw@@L?שSbccidI n4' U(m$%甒$TrEzie]g PE ;(ϘS\\\U %TʹRh`YrϠrIYdKy}Vpp4<c=ڵKA@, cC.^4 nY @֝(fXW(IDh*-79*<pH a.2AL>7||ABZ>;Z䊒WfPPxci=d@pO#-WJ@zVZECuۨvT{v44rDYRUʄ=dJANY  EC0V@Aq98"xȭ~.cV^)z̉IMz<j)^깆;C etE`ɾ$**soHb~ )۩ՄYyp]q1O`Eϵ-%p}n-5NL|] I ;#5lBGAB@ pϵn =B H`G|%e!nj/`oپd/^U@Yإ@Ϊ @ |]>"4@􇻄6z R^/Q=.*At@n"Lp+sܻJ@bጫ݄- )^ =E0-X"@2E`뭿 PCh\ƷێuGК~y*,CMu9<@ii&p|6޿biZ;BJ1n?K< g:#|]ȹRJk_ԕM0dde N:„Zt@D`HvRh?C+A%84pk+/9EƁAH{u&^ `\p{͛7UV [V^-9]I?4'.QJU:D?8Ǻp֬Y4h :|}|o)OѹB$h:IEjj3/v (@]^}ǽSL] y!s>ɂ2 G᱃I~im} @o6:~r>Mɡ}/Xqp`踝8wk\S>p9r.]Dm۶ロ4iXUJJ ڵKݛZlDJ`Æ d2sƍ'2K<|j T'0@F2vE8W馛hƌ4|pdZx1=)44222QFY\!PPPI=??_$5ljQ'x@VA :toAEE lР}t)VН>}&MDEEU!6x;%%%Qzz4˹Y @8$0_d6ާr7(}jמ5`1J;vO?MϵJLLgϚ|wxYrmڴiԢE 8vmctS4B JCЫy @M.;JiӦH Q޾}; 8:t`U97oqOBN㦺{ ]dC1 'PUk gOҨcǎv"-[إ ۶mRTǐßp26l)_>}d9߶ˆg ˢ糩{(A 6mBҊ_Nw)Ci?3[+v&_UQNsRyyRA=*ﶰl29r^ Cl8OMԩcP#Þ[Tt0,OUz `I@VO tY+,5!!AY6 )޿K?Vh1}ǢBdUksʔ)t!<'8y j޿ۆQ#݀G kB$00\Q]]Xqu7`^.nԂ" JY*s޽{ __It6dGƯ)%59MiZwѪx:8_pEeOn jr)i˔o q.0$̮ZBllZA@?YFo)1Ɍ>U΢򼌪#Kv"a7ҢF֤%&зZw@z^1}7F((%sʣ0~73irSX7T1Dy!]8#|p>9A֡)\x?TC =Z?uZxp@4A^)+8Hղ걲xKxAM%S$tv]sQE>lW-P [TF" i(PH $dvio"V?qv&d++h#pz]";~%0ju&4kL xnm;֡w0u}j: D=U'p>xHHބ 9Int{G]nٓ}:Rxx8[/ Q% PI୥G'USLqkK cU5QAO֎1:Upk?.nK`پd[m[8CIٷLWQx=P )n'%%ٳamBvݛZlYS:>J#oWtf>j:^%>^Q'_i;"@ ;GWUf͚ECLǏ3gɓeq`<vS4T\(;w # N ynD@MYx̝qy\@En N͛7 _ӧOӱcoi1&=iN1])1("Z)i H`,ǑnJOO 2@|Μ94n8//9-Zg!ѤIØF!›PpD3 Pi3֞,ͷ'O AePkCCÿ1׾?H~m 4La7M9JCؤ@WăIIWv <P+Tԩz۶m_KmFpR9byZ /ZZzmwm>T)uT|LZ"- &{׿ @@p]+ Ks6Kw[w)"@xx"g+y^.++:IǏ'^s9#""wߥI&ijșK7i7oeR}$ 1Pޱ wd$ G0PmD5~% A꟩9Р)6&ƒCU "!hJNNk'q׏^KF7yAzdJ':<JSOX9rav?{¼pp.*u৪W P!{za^/|M5@U^umrqt{CĊmd2)^d޼y$o&+oߞ5jDSNIWsbXT"yVSYN% ~HbNiM{nݺ Wq@O'SJ999<{) ms$m,#@?ϠuGJ7ǿհ'"?iÿ$$$A|3gT{s >9H>ꗥ$]9ȴ㊮MQ(_%h,0.@^ Y>]Bd۷/q/nر#=Zsķ +11Θӈb*.Ъ ='PN+mG !JѬr iݭ[7@Oe;KJʷkqGak~uAe17x><}# 6q'p7Wt/Wx-X@ ~ 222zKu1Y ?˲ERry۶:A&(B;Er ?/PЏo>7ͭr/gG7|b0Zjq`oՊrqn;/ "{ sPI >J4q)Kect!JܾG̙3͎Nab|6aP*/i׉Fk5a%'Qv)Egs"@@Y^[j.EjXm`t C;t Vr3 Jנ{)m0sy^Xۺ"c|Jz=poF-(a3>@|εu:jU  LUUTZA}OT1rO/A*݇P>/WB@K֣QJ.A* IڹYiSkq W[T  =<יDB˂/vRFC>vp[+Wԟmj%i˾1JT%`9Sg/vQfvP] %л|kvQy,O?m9ٚ D=F͚5?<7\ف;CD> 'SS6{@Yx؆7SNQDD}Dz xˁ=|}TA_ܵȑ#ԹsgZrJ"\SDZwyDSC2 Ƚ|rDQfs̍_9qsZ@X}>SYr%1LTPP@cǎ/>4%#[?y_9^X/n9UPOU@Ncd޼yVmq 8qR08(rA@FEvQ(%x 5̟?_ueܨI&I&;SA>TmFZS$- JMz*oʃuM$!`x/_T7bz+YQR.9vY  P ~bEKߣvA "f"-繹ve\MEEFp^胀Pyf}j&L@ǎSUG˗/')^ӆ b֋}$Ō"onNT?P6rxYzٹklIU9y?E$NYI9iӆ6o,] 88iDr)"Mݐ\tAW H~U*o۶3 ?r9  e)/6 raTqkŶ0Zpnòm~8vƖ F`5_z)??5|qހNqEp;~jۗ^zCeǎ@srYۦJelqz Pw KEnV/7/}֭p@ޟj!j[aR#-?T.˗/W)ν~)45c `ed*hz+2Jԕ9r$u4n;k[A@@7cNX#aSYJ!c?{ w\tD%gMҁ+s]@tfCllsV!bt\BF"^"/ ՕF;,=?A@@Βb^Q5TZLik*EhO }i E;..N8kӇF}Gw&+V9 P'adNvqU&^Ҍ=&tz:@@h؂ef-!|rzG7XܱcGڱc'm[$yTv'.:@@f9պqChpW>ߡSdd;y<2tSCr.ϣmP} <'Zt֍x2 O9{߸F"G Tƀ'Y:RaJ.ӧ6s~O&|ڒ@.%ɠElʰ  GFuT+yy1Q0XAީ5kֈIŽҐ!C2}W=q|.iy 兊,jMJY6^,mTn*(/m*(O9#GsPj,=*i^^l|",=Ki LE6ݞ[?>n˻Sŕ\ "zZ=3ެ:V]/}%Fkes ֭OQRda>^c,IKg4hi㶝zRP?Ӟd5^DhY)(.v2&7պ)!Wu^42w\#ӠxzM xI&r'*VCLk?,=Ko4zhի{Fx>vӦSyr %gI\J-+ώ[.I_;;=~r-H`4O)Er+8-uK /s@eLvJ;_4:lcU׎8t憯4,5$,?_V;|DةS'SnrYG^NG:s@9+bn|Ѿ}{k߰_;r]w6Cgg{{=CQܹ^TѶ@@S"SNN3+Hy6PEQWwUG›HUUW-,ȧGϨ %k\t:9JEqqMK,sզA2Gؾ}%쎹G255UTo %bJ aX'NV|MNx>JϧɾyғQP<');8Ѩ̮߾G4U%!F͉_1G ::J{-X"FTZs{B(9Ql}MtAXߟnjt;iZ9Wj./l۶@^+تU+s;>vMlI~N{wlKԤuԳOzxn֌b  F]'~Dk.[7Rҹߨ~Xp41TK#58+r[2.+mFnw=|duՇ̣tLMʦDL?}?_clzhXkF:c\k`-W!$S~mw){{`TX]%9,{C/ |t7}Ƞⷨן^$W5viNImWAdPiuո{E]GУCW JUA7o\<*{GS7nzVSa%3/ɲ:պW -::h(*p=Zzq@^UK۹R  |~l IDAT 8 OYSb99~mqԈ#4ſ?EsAOm68v,)}ש&$!cx3Mv x|eٳgӅ H3g@@|[r%Pp:%vmcQSΜ98I^T$}UV)e)qԜ@v49;\'׭$zXMܹsg'Pi5K @ڞE1(ѹs>]hi_OAAAti:9ͤV   AyDimW;MNyE2+ Xs)k&N\l)|Mzkx2Y,A2@~B:{`Aw} 8Fx+F9FwmB    `d^hd@@@|@_;hx@/@E    A@@@@ z*_&З   ^  PQ%28|w68^*A@@@ 廃x}+8Oŋ D@@@233԰LMM7CQ0@@@BBB<[`^hԺuܷo3=J~t;;3^}Uz衇{!?}锘H_ڻMݿ?=t! }ҤIhԨQ~j6oӷ~ Q6lG-nS @=nԕΟ?頻p\pSkھ| j֬p m[ddxI4qŸ cǞ K@* xcm, @@@ Fn88xi檒ΝJ׮49 @@@0!` h.m    @     PFvp5zw;N'Nkj(O 4HC#F;v`V駟 / 0#I\\=Z|V?Y~Pm⣏>J}ұcGz饗(99YI0ic6mJu B)6o\# Ԉ?I6mА!ChkЗ޽{~Kk.g}5l;;| J=[|NnVqF# (77^|E>'|" \VL)))oӏ?(mҟg2emb/,JBKhMQQQV-Gy&L@sε3ɯJsgΜnx |#ʁ{t |31m4pY:}9HF~?#GPΝ!^}ᇭ|"ǣ㸼Fu<@ԁz xVj%w}0Ҍ(3СPYv`СVӍn՛l… 6>ٳ'd믿No9x : URSSE^=ts9*' pC}x?uTY]sܥ|JÇ״+9ҪXSyKu رcab -u02uKGU222DDv.o s7neffҲe JQQۇH9e;|dWF^4SrWdC5j|\ݺuS3 <1f z9_N_>qx-'OrwFtvx\n9‚SLj0Ptye#1?3 image/svg+xml iminuit-2.24.0/doc/_static/interactive_demo.gif0000644000000000000000000236644214332717401016431 0ustar00GIF89a1U$$U$$HHUHHllUllUUUتU$$U$$$$$$U$$$$$H$HU$H$H$l$lU$l$l$$U$$$$U$$$$U$ت$$$U$$HHUHHH$H$UH$H$HHHHUHHHHHlHlUHlHlHHUHHHHUHHHHUHتHHHUHHllUlll$l$Ul$l$lHlHUlHlHllllUllllllUllllUllllUlتlllUllU$$U$$HHUHHllUllUUUتUU$$U$$HHUHHllUllUUUتUU$$U$$HHUHHllUllؐؐUؐؐششUششUتUU$$U$$HHUHHllUllUUUتU! NETSCAPE2.0! ,I QB A&<ÆJ\HD#fǎ 9HH%C<˖0Y\IL5c漩Ϟ@y IPE&=% tcˍ$$iͺU4jD[YFE$jI%mnͩbO6HB&m  ǎB~lprdʓ%cYs͗-s,3eӡ;}z5j֩I.6۳s7ؽu 㶅_n<9q侇Kg]yԝOnsٱs.vể3mPsvƟĊ-Ua]4fIvU \SGwnH -w|w߀-xnxx/x?.yOny_yoy.z褏nz騟z݄RbCN$`u0g8y^ .(ZN|O/}S[}܇}/~蓯~~/ӯ  $@:0  Js zˣ\  yK!V.! g(6!wCml-I5Dj]Hd˙N8Rb85*ƊA8.JS #xE1Y4cѸE5]tc3qw|c:z$@r,$"Fzrtd$!IGJђd"5HNJ %(GyIQJiIZ*f)m&ȼڅ=.s]0Ia|1L$NKDaTњqĦ En.uf8qzLŹNrnSl': ŗ=N|';΁Ԡ=hA:O6TMDJQs'F#ZQfԡhH? ёz&MFA~. R14Sf@PJԡH2ɬI,A|*L%1"̤)JJk'֯իh-kZ*OUpe[VՓx%+]jW浭k_JnIlTJXlf4T3Q7r -hG++-AjWֲ-lg+-nw6<>Ӹz<8:WMt]N׭.vzWͮx^-/zWM|_/~ p~_5M$&!5IK [3 {xWeW2D~51 ŀ`[Lc)gc;qs!@.d"38 "*V2.s^nKU(EK6lF4-DyYsLQԨ>SU) Ie\WɷnuMk'zOu-\]3;^kj:yunSW7-r<35l_"X~ٝyכƷ}~oG~3tBB8LCqt{ C.nX ĉbD IT>$p#EByʫMkE?t3Gӗ+}Voz%En)D  cJT^=c']ሸψ_,zSUs$]*~ޥO< ! G4!huN)6/ <pD$6OʻBoz [B@$.-qkďOEH(wD$~$Ҵ9MM~wt_owzh\S_WqS! E `} P j`} (wqdR#ڰ 2 pgS ŧgA&7xQI0wHIwv|gQ0IxׄLNP(&`RgG2s0!P _XwjOAwOQyOQ煲 e3scu{P{T{b%|1|}VA7!nH"f !" nv5wsa$}p uTwuu8uuu؊(8X.m]VCtwn  hqd zec(#`7(*m|Ȃt`$ {E. Rqe"H݈%8#8^xQ\']s:wz`Hjp`Xg"YDE`szG0P5؇EXm+Wu|\HmW8'cQ}} -{pLN qM OpTU9VXRٕWM9Kwj`spzp' H say&"Px((%x_A|R~x"I X Uzs"h~ٙ bɃUQbv %e(dyYyipY( sBE22*Sn7/I7*|H ff7H`ixdىHȞ鞯Xٞu: a`HgY `vIZys`"X%)rgQx2Ǎ2)(/0&)yq_zzȗ׍G| *<:=>FBSP^؆p'"` JmvEPKJtp7hYSC ℈mKjG7Vp qst*xzzj|:{u*yڧjGJz* +$;6vk'K( ک&RW؟u1qttJ5wt75'r:5Hm\I9YJɬšǺѺX,*ٺڭSD #G5ʕ^!ZIZگ{_ az [#zjt +:!˱["( 8X 24`ڳV D;>[*Gk<۴HN˴OR[IW635`;bjA{VR2jK'z۱x۷mk~ |y[PċS0T8T\r2DX"$w0eK۹x7;!vjMG1[(^~`Ywo֗C:+Kƛ+\^; AvkmvdxgGvd0J%>t$KH "0X"#K'i׽7v;'rGkǽryev廾Lz|9$ zgy2' øys:W}F ||A k}{FiH}q߇!}8|)% ~:|G+1טgzq}Igk 95ø'Kk ƇƃfLce9u9$'%EvnŘ".F&1'X{&"+\r+R!R!ېZX!#ivi ij <Ȁ@H'" (pvi {"ג||8Ich1|T ̼$ySK9*!PyU vGlhOb%pNi h2y4Γ,6EEHZG\0g%mדGvsӷQ\*59+% N8 SZ %(m+)}T,-]4}&m)Dzp+ "G2('뜘q{/!;Q ~-(2)2 `D mȠLJ"$5Te}-ɘ̆̈}[ Q[ّu7*yz:7vh|Rx$RpS=vME7)N{cЎ]wL(sc"X }X{PT|p2%P4+9olib|(gmy}qHehvw="~fRh;X,0Vt͕8ٷW(H|U~ԒK"gp֗;aQyz!,*( g'*ޚQ,912_iGt+sjwSӚۭE7,l "RDES`El>G5M}s}Qݕ03/-74I,K.a-(դfJLLќU(Ϻ iZRiԺu{U$`DYf\jGO[rTZ_S/=3 2}6my8z0Xފ,MK_Cg[Պu8GJ%D`^| PmJʅ5o9;/?~ؿe$_-$Azw[SڴsCF`D $h‚IpHD IHB"F=jtŇv,y0C [4 B1U~|y3c΅6{ShP@,hͥ2>e iTSꬊOZr vЦ_vk6iسl׎}lZmε+m޺qW`' ,A"q.?^ Yrdʓ-W|YsfΛ=wYthңMF}Zuj֫]Zvlڳm}[wn޻}[xpÍGà g,(fcZXN]w3$y w^~A3Wug.> (擬<P,\. =CC$1CM7TE_TF W]QOQ{qRH4G$ | k.KFō[PC6pCb3!k# Ѹ#‘4N:Ks>͒E$Ŵ6B'[3KHKLEu4RH'RJ/4SL7մM3p ,0JHC<3H?a=U@]HQ/"2B/7O 0O1Y <hmK6,U$`j#P՟M0y2u^v.|_}]_&/~6.5߂>z#^Xb+^J&R8e)ZȈ.dJ&yMMPH{CQK2"Vnyjyٕ9k m17m MPٌv$,Z2=Sk&{lF[0}mBhYt#qH0Jni<6Bm1rm'Hm4IO˂"O󸓄Kj1p7$v5."t(o)ANKWL4t)mn P$ޠH'W+r`-/4/0击~?_'D x@&\`(VĠ)xA fp|;̢M),ӎ#b!$)hCv4 n$hk1*lǴi6lц$ tcݭH`D![g TU)ࢱr1G$ 0/Lhn*YjZӆJ[t ŔiM[7$hnjQ;MlFqiJ $8@PD6RҔD)UJV1 U'( b"ْ6r2pvBpU(Bd( +`![+"׾I"\OfaZD#\1L5p!h$PH`L$$JZE[KT"9lz ! =b֖w 7)l9^L #)JMRQ--KYҙ+ũLsST ef8 N2J W(Ƅ U R1䶰#*&^bEF2+MĻBq:[I0u9tj# E쬋ZdAY2,AA52c)3BqhIWҳgEZҎִj );KA9@nͥ2CbGA$4seKӆt-ȸS@2LHCOع☼3gv Dl16EA'>4{NL^%AhʤUqmm`&He0aOp3` wNta#b xtbZI FYޔ~:bp5kR%7l!oɯ>̪dVv4k] ^Էdp WT -dvZ4m8[L{l5@KcPLM*;pi]ZF/э%})P=F0Nۀ^uI9ݐ5`*9k;6µq3QCZ?F@97')AK5 !CW$vJ1{D8!TձRP[)ytK!I`E>Mw %E*$^cU{l{p~ey?͟_?˿Gbl򖟍f@ڐ3DTdJ[Y%8#9@hø@h@ @ @@<TAdASA !"$#4BKɼ +1'tc1B*+|B)/B+C/ C,,-$43B3TC7\7D8C89<*[9$$@AB$C|@Xa4AGD qJ|DIHALNDMMP$LOJHiKHK0WtEv5c"Ih9]>@9.!x:vH@X$>? .[WyюUQeQ ُY5ْFHO ū) @z[1Z9[q[Z@8`WIZxڤm蓥5,J]Zhх،?s]6$(ZZkֻQlH$\܅+ ti- $J%K\׃H$"1AYeWՅs1J] S=MWI]mt)mXJ8 Jy)-=@[յQ^] _^a^VO͠*SMٹ98y?+#,i_`:?i婅@[[7 `ieU YH\Mʅ%" v]Z[P\% i[{a|V§|GJĸ@CQ >?^yPc\.\@b} e1>^ŀ]t,㎞8ЅX~ U鯸a>^e\8v q\ݰG^W:^ `e&c< _j_-[e&+P=ː$y8u+ZÒiFfi$M50,oVaN&f&\ PZ N[1bulX|FT_|碉A.j=mjpHm \鏢&W@%C}*0s]&i6nFefC eBvd .]=]`7A]v3]meN(۷ ](Zn&=Ypp =eY{"P@"[;3 7`^}e]%>&}kŽ`8]? nNW/qkf9H{8]\7;dzk$]%Xɘh`&PX>y]6e0W<rF陙BYXv{()U1)ڻIק]o> PҽȁlkNeׂ&A'Еt(uU_m݅Ql2&e/vճdxcYO+J0utjrErfwsOȇ$t<@[~/ CEc ; z; <*/ ~=_mhmdI Șx95>5shP C.'Ym:^ _ֵ㑈!W%yxRMReQz<_JT^[Nߔ6a9wiwON~4F9aJJ$pU>$ER vh#- G]՟v㛞曋ꝘYj!muwkOwXٷW}_gV WUy1zGȷۯSU} ߚyRٿp ~ٯ~~pst/'}uWg?w xG}$h ƒ 2\!ć#Rh"Ƌ3r#ȏ"C,b!)Sp0UΔ e̙8m҄sΚA}ySQId tOM*ZSOB+XbjjYgm-ָ_Ǯ6-ܹxuw޺{Wa‰ e x_č#+\cɏ-C3h-W)0ɑInذgˮMܸw /Nȗ+t̔2Yd::M׵g*;D$5+8;8sdo ;)fso.礃>:ꥧ~ꭳ鱯.>;4Q9?#Q$]|-ҟ~g_={w8{/>ۛ?~_>SN_??,3g9L d(RЀ, wAj+^Ѓ$!/8Q. _(p6 Xtuk}?!~D$8D%qNl"D)FQ}Ad"H"^!o(0qf,#?瑠%9ڮoH;1}ܣ A걐4 ꄊ_<#4R%3YM0 @@@ a NŁ$H8)eK,S˪ E$`2e1WY\`GN2sk!S@wfj’m$LDbfPJtǜf2lV<= ?Q^q>Yy1JrPE9L!PN- mDE[L iC6@xnjKh(@irHjR  ;$QXSކ8nR MlB) LrjH3Rh;SJ LIPO fcR"X'lMIdD*1%..ē.-..a;l`R[l") w` )Cd7r9lK+ Vv-8a%POJaw.V[JĢh8 SB. Tnr]"ҵV.HN*䴍%Zވ x@.M^׌bDɯ6$qlb!ȆG4*XH^JϢ4j?K6,P۫ fQ[a D:o4] Uӌ5+Γ;N?mJANpXPg74!%*г@-Q]&8tVH A H$ iH3%66ڐH&,.,$\".X,KgIXͺZB)Ѕ _&mT4Imde0gZ2A-bmZB.}$ʶD N] JE^idc3[}$w ݻEdY X⒰.n1j+ץtC-1V555!$B@ y:sq%pzPt RB SD6V P !x娪 b߃AHBMճn  { ރP#p05I&z:FB L=HLlBN Q< wG8KC_7](Fm!%@oJMlϫ&.2@Rpr$RTm1E ]~Ml(Nje$ >Ko VvBC8! I4 6\*MQ"@(] @HN)A(Q4G L$ALUOQp{Ղ-6 <\u N@@$XOB-T5Z9@o!=@$.TܠОL8.|V1]\ DceS.xT^! J̟|/]\ [Xdk B8=e}o}J:&ʞ%eaL@!NL\<[bdI%%xa \/XXbL>p!-&R-΢-"."hI|J]0$_MF؝U Ԡ ND@A`DI^m\iE=5"X5FABB&́A,UÑ<)ejPEL@5A !9c HAdkHSYIДM#畀$^ QIBD1.О앞BؔX e^HnԁSEYXF^H.lI]%ee8@g)@WN+vG.!A NL˭FwupT{m`qZ|ȥ/}:^0  +mB])C1##*֗|J׭)V! \Bb&؁L)D@0Iy0\ Tp1 bLȁDo@V*agJA& St& fg&Ylv&ineKl"Wdy.(A(N.4 VYaN$[ʜ' hv A GMߏ] HxAX3Gp#lCj8„\j4@d.8[yB]D[cB0B5A0]=-EM_E^T,^$J1y-hEGYdL:߂-hC,XPl3nidAHm%D9A(BL%9I)MEi5sDWBZi%Eޥ.ę-DV*Vj @X5R}A@b[HB#*_D]`]ʥB\kD 0%k SjJ Z$k$E)A>D "Tejn![iܛL89ٙ,@4#@Jځ_qhjTE'e,e\\MhÏaa-y%l&qoa 8 v&~`"&lAG Wԛ(J+ۼ% !͡b-lj6 gbJ@#ħ"uju-۾umέ-޺!#v=r$c, Rm}MʥDB [}%DDZ7J9%$$EJ:.-lÞ~n*.) -dbjCMD'/MABX-xή DReˉnթ5/>FB.TB*ނnf\%6ET#BoQF^oE\VJz^10aYSZ/TUC5%*АaQ!\Yw6{@K!(PZq^T$ĵ4ٰO ܖyW&awt I\N2Zh01ߜ100DGycG޽ pm=A5 [oJ% Ɠ9 3<"L`ͅXe>wEԂ$B$@A 9?kZB1 7 ׵3>//EJTtUVgw`GVMD֝0bg!gD>-">4PPPcL$0Bu{wT5*WkLۙ,$AKW0]2R2qg:` yycaƄj)@T#}8:GHbR{LG\@5n7+RȜ |HaGS mϩĝ]+b&ȗD)YD\vr3;r[O]ĄFAd;@\{B"Aoĵ{RAbe$C45:u^x|GTs{-˩ﶛvl_mFHo~dv-9G\AF{G$3D^!ɏBcBDB6F[E$XD÷F[ϻ<λ?YāAW9 x6&mH#D#Ȃ#dx\#AȂ+`=֧LjJA,D8Bp}S,\^,F6GO<+~Zl f er D>Ă$ńbJB\/>y &kǧZ(#a]zo8GL0>h{e,f^}&/ŏlWY oվ^i ῾BJh %^}ǨJ \:/=MvaվM-DLG@fK@I$J,h+ $ @rCD!Î#~hqH!!$K%_$)J6iL9{gQGw"RB|*D', _;VlYdU{vZG\)ҦDWq($Ҕ@V"i$%$HqJQE"l#fmel04 [ز9HJlB8іHJMbIRolkt{[۶$alCl ܸHjytۂVt$˳M?- Aٿn+J@" mȎ5+/:m:,7(d4RPkn؎83sK?kN$.,"]+!&*H-,Ȫ$kHJ ,$ԏI [? "šZ~29嬓;=? жh8z#5ѐ(REmzPG+-$J7-)Bő2@,G(*u/es%"J "GXՑD85IlTN!]l $ISeyÆEvJ~OQ(rF9*A6K(!"42"goA[mx.t1֣e[mn9#kVN U 6z w5 $H%bI6lkeg @dwB"N{6Il$ZsrʗͰc8C3D)X46nkI8wjjfokȡHHwKմ[Lw|5r-os5XjmB;'z$Si({ێb}FJVMԐO`qR&wnx IiBPXh3"P,ʋlF[=A8mF1c4cxFBy,YY)$$Xǚ@HD_*e%EV!z\%j>Ɠr$V~L ʒ8vz#. Kh;:/8 !DQqM:7NO#6!ugOF$-&δ6XUnJ`ߙmY6S=mDZEK[~@YT%Sr6dj`XNc2$ :;S|tC@+ I@%Sie@e X)[$N1 p2ƕՙF9ek4}[,#J$! pLyXg0e` p9N0 K ʼ~P+Jr81i.eY %1Y1%s$TB l8x`& &jL'FC^ %$Q `Cծm٥z @9l̥ iySp7EŜCl jk:kpk̚MCuU$>:ynM7ap.-ntN Ȏ[2Va%NTM\HAZDzUyR IV0$]CZ.*?)$B g[M_q]湫z;;pұC?<1⻡mZI07@ԲƵqf v{e,v<3.–q%A a%Z~I.xll-ĝ&鮟ݡ̽Ann'那ޥK"؀Eyg7V*FiJT2S7U] UAjWp$5,/A>_Q P;/Zɕ#L+3%̓RHpFR:婔տMw.Kt* ;&J$R|hH OO:0-\0E,(%K(#ʈQW/hPc_pqpgkPq0@"B HDJ(Oj#5F͌0 P SOC8/ ӈ! 0p?B(uܭM׍T#JS0\2ee:B">qP'mMCu hB"(Rd$Qې (D_v q'.11ܐ*fR<Սe' . l m }wKQqݮPb p1q¶1qG*nqn&.E^ %4Q r!J !2O!j"%1!5")r##!=#;#E"I"G#Yr%]$I2&[&gR& >O''}2(r(((2)r)KeQr*+,r++,,,-R---r.2.R.-/p2vJ) ) 0031s11!)1^-ӧ52op33;3sP4}04?44U4Ys4W5[4as6e6S36o6s635{5y4u7q8S87s78S 132s:::3;s;+ //r>>3?s?=???T@-$-:AA!A%4B)tB-B9o2Bm&_CA&ECGCM4DI4EODQtEUEaEeEgEm4Fi4GoFqJOBCHH4ItI)*>TJ tJ@J @KK4LtLTL˔LL4MJM 40TIINN4ON5$*8s939PU9P Q8Q8'P!RUR5R7R;R=SE5TITM5SGTGa"HU4VauVeViVsPKNMʹMtMWUXXXY}uYY5ZY5X=t~VW[5\u\OhGuGٕGu]U]]^5^^5__u_U_D3\\vaaaIlg4[Z-Zb7Vccc9cEdd;dU6eYeTNAvf!fifm6gqV0"KS5h#5UQTVUhhii?hvijjiVkikj``wVgl6mvmTbųW[6dde6ovo3veoGVono Wppmb[ymm7r!wr%ѕ6``97s;ws=7tAwt5t?tEuItY7u?lurgVdv)vm7w <J 7q6 #xEKxcyMpwxpz{WzW{7{`6,̶\ br`GwWww~wO.7P6lzX➼vT1 ! j35,AP$0Wlւw+5AE8Ixk1؄=ha7w\' (!tj@vXR@!8wqƓXRd@ 0.!FhalYe18)Qp%2FpQAS@"rrQ^J@ڠ, {7|w{q99)y-swN˼"DG1."⨇4cF9G﹊nN,',hJT#V8x9g@W]^hp Po2[^CT4\xB_؅>>dDab#ZNa@ـ$@ r\wWU[%:)z-:[DGAbe5*mNp-(W5P9r,ԙҪ~,(d<予8W4@ P9i!9Ya6eeZVqǺdmc8>"en Zb68›[fY," / bT/y#%-15{w|<$D5(´p;}JP4f~ ?D6@>d;C,b!W;˱|PnlY@[i#RJ!Di$FפRjzg:vNf&E"nuXJ%!slAuѯc9/{-}=]59=ӧ1c6^'Ƶ8 c}}}]ˁ +Z`EQj&+2I!C]zj I;]۹=ܿ}uY5 Vċt=1=}};bIYNrEc S3{>#%c={=}5k;\E!|9MC;Y]_c[m^og*B}ުI^.>~41'~Ӓ>~߷>>oM|]|5/ۂϽOWќZDE0#J1DͿB+E] r%nje=~ͽ%3_{~av~$K|\MA_Ph,2+i'E(.8/$6/P]S*AV&C,ZF:HXlq8W"qZG1"Hq\G4Ťxq%ÞhAyH,8A 6LaÅ)N|xQB5FdNJGfi1$I)M<eɖ4gdyS&Ν:{ 3ˡ. QBLiӥPN}zUR[,dX%H-Qٵgٺm ܶI"w޲u6I6-kK^m޲Ɔlnk9r)%!ᵙ+M"1^Wjp[y|9fI)۬+3oie=:9ʛ3<:ҫSn=;ڻs=<˓?<۱\hE;o lg9tKEbPhY}e`c "6؂ڴ$@B6^$aeԆ-IW~ h$'P *WhjhV 6FBIH$pZ_1Ph JZF@H# UPc 磘񥍂}+!1ϋ:}~{>g硊&(:hBJZ領Vi~^ꩠf:*njz *z:{ W\7&-蕄6۴{il*6-mlVYHL׸D5IDra2"!of,e֛?&goIH"X=+nbU+F5eh@Š \9km|Lnv`M½X>3=s@-tDmtH/tܩgsVVLuȶ _ !mQphAI "(mц E >P"bzN w7Q f~ڋl8Cj.BrKc 'VE%h` §@|,Pe;8Abd3 @b>: <֥{eVuT':YaoSƴ@g_fqlI+ނIiDd#*1L\'G~ga 8i1YԏH,=ud6qO@.< @EDm@ۜB8-$@`eC|n]$,o!t6!Iw+QHn@, Gl QZF1 G mZ1id"sTf3ygJ3tf5ilbsf7yo3f9itsTg;yŪn"ьuZf, v@d׳@b2M-Qb@>e qjLHZ撍Y :Clddmj$(_ dlP˅E?*SF}%a bz9-2%kAxuH $ he#j"| !I 2;`(_DB46.QC@2D"ykXSu[]NWv]v/x7Dĝ{ ?r`j.|LwӯI}[ ~[ /Ǐ~_Z>{W/lǿgkGO{LJ xw؀ QhxGV5g{W{U1fg&.huoQ 3MGW cp1P 76{%Xw6LUoz"] q5WxXKHz W8YhZ]Ȅ^zr x4mACat-p3LـF" u3E9D ie(d(nPl_{)`U=7Xvb'gL5Vä6'Lh(Lf` 5g` vD\8o N hN)mvVG\(tVJGq u#)0Jϔq`zh¨Ȏ縎؎!Aʖ|LOsIo!qq.kx0qrlboCgА mIؐpA`Bf tP9$l%>PXXP(QPQB㨔gAQ(VdSiȈ@ٔYpؔ`YqfMp ?yrANiP9I(5>U_H%K1$R7JXF&R`.o!Sb#uVY9"CKF7ygj[&6R9F Kr$ mP C ĸ)1^2$4Z%")! r@P d A%=Mmy4I& Y(pmYӨqPh zʟUOyMZfѡIJYG*9"RxEP` *ڡ8mYh\CʅQڃSPJXrF$!1C@),բA2hbR-ЂX2/}%:R$.wa/C/AH4A` 1QH!#2-S3k*0}:3kx%$rPSI A3*QcB\n6b2bc@/BF6rk 1hq 9.pЈe)<( wjpm Fq`͊ kwIkZȚں5mPr*(uYqЮqn1 tIٺj|ך.c?4tOfSdPې_jR:C@(t}C %$,!(zU؜b#":T$A_)s iP&Y ɪqA%P-Ȫ% `B"٠ j!wƇAV]l pjш  zfWNI$M-g~<{mQWtɗH`حjRy$z,n#K55T+ _[kEnY-QHʹab VEEiV:YZnUKn:R-)0b}EiX\bFq$7TmZE(bF,'Wde-A,Fa\ǹ7D勭h(8y){ڏ6CmCwR GLX(Mނ&G !HpRܸq@y š :q0-8K.8H꘿"C&ERU _1-&ESffXja~-=@!& _j"I P:v21dcIf`UDP 8Bl/)$@:a Be! Aېm!v!D ZΦf 1L`Fhn߃Rp- 8Y:d(ߥtrLqLEϢk`ɯ;0d P\}mg,fMQu;тVт+q%Q5VT ` L kcP|UV+=WDOb %hiMz{+m/$_}OⓆ_>G?>X W jf "lQu_|$J$8`A&,Cl-0I$mRDRQ!$ )ٖģH'U`G1WL"E&2uٓOA"M6q.%ڔSQNZUYp_ 8JD`Ӣ5[lZl՚[ڻtmYIK8_Èpcő2F yگJٳ-7+mc bH͎klm!D_F5e5'q N&λ]{][zN}mס 9k|σǟo]>|_?~ܣ40?kBkjB*B 3 ;CCqDDKLqE[dEcIL-Sˮ rG4R#yDrI%,')+K'I2J2432<4\02LƔ;4KN5t;{H=,@IN-lӐ>-0foiN0JN;9McX$8 2#12d r H I3h77A?K:Ks=?; cA3`;˹;$3@BK-S=3%G>)L07¹$paKCmAKzEs BZ0⺡A[ ӻ6?{l3102pʳ_lG0;vG5rT614 }..4ȌDH\4rR2 C -l8DǻXF'#ă0008[`;-('JMB&9;D : ;\9F7ɂ:ĕʱliO"O4*$[AKd?ϛ9E2GBRlB>3 M4t4Q?)۲A#P;cPUJI85k9| k93[&Ĉ 3.9ǃDNcKK)IИɌ#@< {=<(k+gB3LA%LBuP{óH`'C!4 6c>GR?D 3[;iDYc9ذLe %; HŐޔlUP,9󲗻˳XR̴ ȳнK+ <4M1=FZ#@(U=貸`SV`C~FL HOW~ DWWX+WZ:8xa/Ilhཀྵ(;48T;Y@-TTeh[<VP)1Ҵ'!Z" Z"Hsq? W稯2zE@w3A<ڲ=E[M[-@ ,Y‰R IZFň[jH1ZlPW-du-ژ-z\\Mܐ ]%.e]]e]m]؍ $=e(U۱] ޝe۵-^^^=ޡ:PI׍؝^=ԃ +ނعꗀEX^5%^E%=_5ߵJ]%^^M<2k گ]'1ZNެ Z d^VUv`ߕ F^ & 1%-$X3YY_^a9uۿmհHNђ8 |hlC!ӋQ3]}a%Nb&Fb'.](^'(N\՞'ʒ#Dΰ.:/{1IɆhP?@ea7Va8~㪘-⬎ M=֗84ATD3jȵۑhDJ,s1d3?4[O P`Q . NeT^eRn8r} Y8/4H9K2﻾=SRH|8+;0cb.t9/˻/;-Ec>Z8f9gq_sџ ׈(F {=z+Mj|4<4x16h {fŜQ*_5N_#ִPM_h^h&ei}FA0|8&D Q4 00:1 t9^5$棎0^B4|qpk~[=,2 CRB<=PmGܣYE7K 4 26H4!`4FUDU[cL^Jg l`V`W>̾`lllm$sCF cԏ0csfk_jNʦ4dvCBn56 }Cjkng-ֈτL ӑB^ &ǽ $U[ @z mIʼn b |O"jO*^&)npp wp /.VվĪ s+K)32HX|q4-n- ڶLmѦK=D0Y$wn##_]:f< oUʋuȋ>Ҭs3 4pNXμXr`h5#{Q=>'?tSAtU,XV`"a ,l$GrMt-g)\BEpV OvVW_iZ?iuXu\MvN'vcB?tS˖06g_ji/tmgv?kO Fcb/wr?w pvpw_wpxwyv{w+I)ItwsxG%nvkvv>vwxx2JtYxxxBt]]y/yWuu?\_yoygyvyydɃ'G?zO_z yzzRw}xwzzz7p~O"x{{xgx{{{{.x?G|$wyȇ|w|̏ɿ|̧ͯ|/?}נ٧|ow}دק'|XF}O~PG?~O~G{G_o}1[X~~W)}'/m~d"~BTΗ|@b`$4D 4B:cD72DG,ǔ"Ul)%͕6cy&ϗ>It tТ3w=siOJ iRU:kTT2T]Ǟ-n`Xv⽫7/߽~+80 #>81ŎC~,92ɖ+c93͞;,:4ѦK>:5ծ[2nmQwB!}F#Gn29p彏7}:sƫcyv[.|wߋGo>o_zO~ _w` *! H!bxfr^_V"lwIb+"/#3X#7ژ#;#?8"]zqqI~ȤKB"IM^Zbe_zfcrY&dijfogsY'tizghY(j~[5dY@b:r:Zl2T$CNx"Y决v :8"[l2F R{fm~v;Z[E⩀]Xnڛ//0W]Y-)T$V>Ն-I|c sV",V'2Y*$Ì3L2ל.6s@sDLº9&QWMͪD}WAI t^/c \6g6k67Wmmmhs6- }}DכֿDxF7߶zpb~vsnnz:裃κꭣ{홧~;Ju+M"aHPIW1u OL]lcx !߿ ?#|$n]BI&y=PDI@o\- IHnp{DApQl( R 7 P!?(&)ApW6ܢ^%0^𖵺O^l MK&BR"m7ڸ*݂'1$$A1I\P<!E$èQEI( v[G=c GCRw<"HB.2t$iI͵PH0 c/7TyrKD*r%-giZ_싈~3/ .~ ^X9"{S¼͑xżk+/y4q '943D:fNpsTIu*SJիB=CH$eRbXJֳ5[آ71Ax5z]!_K׼`KX"5,c Ir#I˪5ݬf;YtnobTH&byiqXvml_[r}by6AH;լbUKM.Kh gEYV.w]5" gP98]oz۫^ ]o|[ޗE~k旿p l# N0C0'l c07ԼK q,m(Y@i0q 0c:1w 882l"=1&CRFe+OZ/{9Xs f3j>/rBRAwBBR?(4KPAF]C?~vt)hHWӗ5=iOӂ&]jBԧNt[MjUզeZzֶfuo^5]P66}U;О6 G:z~Umnc;[u7 ;^7w;7m{;7<8 oęs^8k$JIP[ϖ.T_ Kg!O9Wr\yans99o|>:Їnt#I':ӏ7}N:ԧn$:ķ񮃝+}@sr%0oH0m=춸t`_| R0w<owWGG΋>_}Uz֓>=eM=}_|N8ÛpNY6 F&]B! rPA|/9S 'x? ``"*`2:`BJ`RZ`b_qR`tّU-|i.8޻.XBیqI^ 7@Y.]9BaJRaZ=q8Ҭ=S?ťlC\l-ayqܬY\ @5_ 8˹?aaȵj`2":b#B#Jb$R$Zb%b#f@?u"'~('"(SA }V́6|-@!i~76C0Jb0"...?dO0~ 81~6 "4Q7B14m_  ?c<=#>c@@@$AA*dB2A6$B:@vaEnD_pn۳\_Y$4%|-4`$?0c-L6P\N9c4%%$Zl3Ҥ.@\B ťQ@HZH?8N\ e7|ŕ?\%7%e&e[[e\\G`巉 _m5j]˸ifA;@8J6[J(MAVhRZh"6(rv舂hhz(苒hp]^z]DBNIl*: fwXge[Xh&4:݅;%@|32]&c]&fȁ7-a)@^ߞ)*)**.>j2j"j:DF禖T- ^ՂFRGZ%lW!xjdyZy~CdL~e"Q%9ge *ḵ`l%9b_]Z]bjkrz+$@`Gk_+ O$ըhI)IŃ]H%g.&:[gJ^Aq[ d7` 4gX8 6 ^ ā{.Ujl)4ec4:b!/Tޓ6!2a-*m2mӆ:-NF-lbmrj^-fmؚؒmR-~&EJgۂ-8ϼb DSHBM$Rؔ[^)b*Ddű m R#gu7 c/C?\WnC5yv "ř&5e@ 箮(艊 o/n"/*o:>&Zobof"z⢢~orE 5DI5I-HBD45Qf] Xgl6M^!>aҝ P*&$BF$DCp 0 p Ӱ pڱ-0,%DTk%D BhJk`pP2]"~kwq%_N1 OCHLDЫAT0قofU^H{jA-nZ2^'G6(r))r=۲n]h-Dj!H+م,1ʕ[٨.G_2 ΑV`4S3YU36_qs6w6{s88ks99o"1us;soDRP|$YYjpB"4 '4C; 74D? KtESEG4FOtFGDE7rO *^2rr7M*oXqqOOtPP uP1۴RN<{ 3r!7S 0s䫪 tf5HYuZc$usWuQDN@4%^_ `{a 6^o]vobb;6B6boL4vd+eWdc6]_fVcK= Z6[vjFYvkkHFߴRn뗀o6pvowpp#q+q3wq;wr77t?rOwtStcukuswu{wvw7xvwxxy7s3mqS'{w~p}<~v7}00'^['q88 D8DxC;xc8ثkx,@ 6xxCmI{8n#7w|;|縐x+qնö LyS[yckys{yyyyyyyyy zz#+z37ژӠkzms{zg:wz:z:Ϻz:ﺭz:z#+;/3;{';O7{ck;os{{g;w{{ͻ{ۻ{;{#|'<3+7;|C[c|g{~烾>~>~þ>~|O>+C5DEE4G%K5L;M=5OEG%UeU\%cuW[UV\iMV^gH`mX]WcM`uVhVZhʼnlVnpWr=t]*@ xWz|W~߅H=`S R^X fa-b1XCڐvWAQ^UvؗegYfsƹڛkYgy&g biuN饝nꧥꩭZ뮹[>l9x۞n߾[  yÃ6or%7r/|97ұ6UO]oe*mmuϝ}߅7?y_桟ꥷ>:{W\?rO?|5_}هsqc_hpx D@>Ё G]0{`9A ~`A8B'~SR8?ޯ1 WІ!y(@V XD"шIDD&ib8E+VWb)(C#o(1ꦌ?9lF2pdXG1ьxTG?v H>,"F򑎐 $IIGFҐd&Iz'7IN2e*OJNҔd,WYKKRzDU_C:/9Lc <2Kg0̔DkVf6MoV /H(89NsTg:ٹNwg<9O{g>Oh@ :PUhBP6hD):QVhF9Q"xHYRԤ)EJX$1HL2S4N#ӇԦ5ũP:ԝDMQT>KS ՟JTͪUUV[ kWUh5kZٺVXmk\:Wt^Wծuū`;ؽMaX>c' ٿJ I K9RvmhI EԨ1UWkMX;ֵխNe[vm׸Er:Wt;^7صnv]VevÛ%z[񶗽5{;w{_w} FS Lpi)y}k(׹kb[f2lf/nMZ[6`=L:X;w=n >pn/-;t Mp}\7p/o8)>q'SkjyI>rLc.gg1i.s<<{tI/ґ!(ԥ^ụֵuP{a.veGv=qg.wuqV| ?xsqx3JG:?yGB+yw}Ixџu ÿ}^qq}?|?~Ɠoʗ~}}?SG៽?~_May3~Σ<%?>?ObP%&P+0.,019P;pCGKOPM0Q,ceg"b /yP  m P Ko o0/ ѐ O 0 P  0@  q!/ Q0+;Q'10OSS-YP[pcgkoQm1qyQ{qUW1Q(bE9Gq 11ƪQqۑ 0Qq 1r  !Q! ')"+JQ ;#Q#Q$ő$O2$A$S%UJ#/&g&kRJZQ'}'w'1({(R((R))r(#nR*qr**Q#&%$%R,Ò,G,Q,Wr,Ӳ%*.R. /r//!r0030r1 !SL r./2358-4,C3E-r-I5G4U,ٯ|M&1656k"A ĉ*t3'O )r8s)8)93999Ӧt:s 8cJl_P"oB':9[U:u\ŵ[]U]E0L}3fFgQUL`LL4XU``E:bGs>FB''+Ic4c9UU;TQd=vcGUcKV4B'MKCʜ Lt`g`kf"CoP IKKIKhhivhiiJt7Z3*oflǖJ25U?VdMd9mvdmCmnV؄lVlVod[ӕ\\pppqWqp`o'Wr+*354W8<ל@WDHtt7uWWu[sYu]sawve7tivmwtqwwutyw}7ucxkxsx{7ywyxyWyyy{7{w{W{{{×|||}W}۷y}zw~{~W|W|7KpsI])xؖ؀؁i )x%1898=xEI8+X-QUGvo؆swX{vHOD؈XP IhxɊ8xX#rӘrX‡ߘX똎؎Ή؏yِ P˜ؑ؋#y!y/-9%y+AژGYK'8WY[_ٕ9 oٖsyuwٗ{/œٌ5ٙYyhLIY%9YÙoqyY9ߙy9a999ٚy9 ZfYiz#'Z+E973ڣ9Z;?Z׹Z :QW_YzUoZZZ-ZڨCZGکKz:?ڦk]:zqZ:iӺ3ڭZ㚮}Zzگƚ:[ ڱ:; Yڲkr7;?۳C[k:S۴UW۵;!{)۶ei[k'y/[3[{wo[{;۷{ӱ{; [۾;ۺ[|c [\˛|do79;ؿ \GKWwI)|e<_sg|a<Ã|>CȋɏwX\[ɟ<ţɥɩbku<{\#ƿǷ\̽<\꒜͕OΫΡέ\\̱|л< Ћ]}\'+179}=];]ԗ' }SUWagk.1=w]וӝ[[#']3G?C KOxW1W+k>;uA^E>^kb~e^v~}y뵾y˾~u^~۞e꥾^>#>~ U^?)+{ ;?=_KG_%__a_53k?7A_%bߴfc__uy?ſ͘?8䵿_ 0@I,HpaBFLPbE -fĎ?jIrI'E\Ô/Yt9Ǚ8k漩'ϟ!w9hID,TPIaFEjTjUMf [jKvYgŢJD۷n+.ݻvݫ/߿~,0Æ#^1ǎ#C,2˖3cެ3ϞC-4ӦSZc׮cÞ-6۶sޭ7߾.8Ɠ#_9ΣC.:ֳc߮;w v|ӗ_O=zݫ~O?~&X6 :NfXv!z &X$"/WOUZjxSHc; ?F#Gd6:OV9%WJ%Kj%Tf &[ZYf~jlfp)gtigxީg|g)h6wh.h>*iNji^ini(2HꨦZꪨzꫪ*+J묶Z뮸z믺*,;cg&&*Β V+^b;fvۆz+nԚ[.-;K }j0' 7G WL'4 "1'r,2/ 2\鲋.;۳s>}J73@?ʹKWT$w^ bMf6]cvp-wtmwx߭wuܲ1msn x7x?x+Xg4kP>5wn秋n5稷թ⮭lm<'7GNg/k=؃}ߗO_묳O>;?׏?۟'NIx$:|#X(YPz3 z 'O}'4! S/T! g( Ȁ!w?χ8 FLX"2H)ZXXzQ_ #(2f\X [HC6pplc(:nCD:H#GҏIDr#3-Z&3Mzmh eEIQ<*fG=r|,KZҕ%.w9,HNґ\/FӗŔd2if*,d2;IOZؼ6m \8Iq<' j[.9OygJ31IЀtM(Be:݌7%JщZQuv )HG*RHS'M=UOt0miL_26ԦTMS攡?)PIԇ4HͨR5Ԧ2N&IJՒVV*VjJzu_XiJְhk,Vo=j\}:סw+\ָJP ,`+^\MV2UY'ֳf-vU{ \VֵMhZa KڶnYpKxMf\~}Kcֺv]n׻--lKܚw=z^kÍ/|+ů~7 3ӒZ 0 `07 CLmM<*vqO6u,a@ ?H>dΔ'2,*CgX^qe.s02LdydF4?^3$L&۹x3g+πsf0bFh.Yΐ.3hIGZ͖47]lzCMjԂFSp*а~kZzt/^YӾ9=`Nvf3wv-jSy5l{6rMu6lzۻ}m~Էn|'޽w>%x;:oNvaOvn7/.[:^k{s_Gv7a>S޽v<7WGЛc+c% ! ,0!HIȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ+KJѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ*УKN. ճkνEӫ_:˟OϿ(h& 6F(Vhfvv$hʂ, H4q6#Yn,@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*G5jI*H8Ҫ뮼e*r%+\,e>+mFΕRcŜxpݖkU+׵+k,l' 7G,Wlgw ,VKo'ܥ X 3S)A.83S4A4;% JD =AVKXgE5C)&@[g-6AȎf}1glcsp-~@o˭k ߀.n'7xMu!aUca0iސF,zK;]>>D.h;P.mZm6ȫ zN26Oz󺂽]'Pe4z2O~ʬ6pş]o HL:'H Z̠7za४E?(L W gHÓ8!hn@ H"HL&1PYJ81#$FBXEĊv״+vQC[ h"q!<~'->D\|S'@L$H8'$'IJZ̤&7NzPBIJ]ݱ4#*gLJP0P{ ViI$yQ [9#`|.s &Z@2\%ҁ^ic"̦6!mvnޜ-IrL:v~ @JЂM迺І:D'JъZͨF7zA: )ܰP(MJWJÒ0LgJӚ"di6ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz` X[:ֲhMZֶp\J׺BLv5 1NÿKUĎ ^#KZ&f 3zb,hњV5 87É]%\lٵC[w pK7<29}ѳkνËOӫ_Ͼ˟Y}h`&G& 6Vhfv ($h(,0(4h8<@ހ0AIh!BF)TViX:Y\v +A$KQlS#QVКY|IV*hE pfDr$a]8裐6FBFj饘24I~VQN>$YjqtF$K,*kz6D@IW++tj6tkMD*fvv$aߖk覫.d+wkzѻJT~*V:[ km9jP ܦ dCB4C␄kѽBi ScpD,:,@#  Ҫi3)<ma-jYBD`e lK拘AJQJ)P3= & Cf٭R[F@.cD 4CJ%AC:}ImPC:G4:~+Ժm|c_N;m@;S}E_ˇ]9P:3ta/7A}(!T<>3HPR$sԳO[!8 G9L.!9/ԼPQ)ב)0LJFpMHb87L3# b:'"&;۔2I)z=RLSCR6w<qhP@ QAxĂȱ"NbSH`icHBLh&H"B푒4\zFDBԥ4aZ$McMtStK8IN13I.&WQ@ɲ p%![;hρS Ee T)7.Hք0"ɪl; p#LAa2#I,Qυ #*sǫI"J`T1W#z%C'WP"=Bf}K$dȢZ!Xbx Q2 k +׮H>2 c0gL8αw@L"HN&;PL*[Xβ.{`L2hN6jeDX#4e>'IhψNs͞왮vˡEE+RGO4mNmZݤ5߄$ <-nzs&ژpH,[cDDtaND!wsfo@d՘ME=cLsWv)h bE%SGlSTiF4}j DpCTTⒺƬ@>eIF"^Gi{Rͤ"bF0CIuS:i+RgD.H-֋K"7H~!&mtU:ZN R wqJK]zr^H#؏$&C V2|g)穟*Cav.>u8<M5p]neoe:!E-}zPIHpt$9@{n+Ii "D)+O~PhSM}\yxE.u IGH~_tiBo+} p_ jcZWEd \aF*b7g']-Wi7{7BbxGXݱ^=RubYlW1~s0[|H4 Q-v/wOvm+KtqKEKN4R$&EeHL8s1'Q+,(gZdYWnUVDaJdXyGg"hjxq; gaJ uJ~q-t+}Sw1Ȇ؈8Xx؉8Xx؊8XxC`k(.[@(v&Cqgex8y(c$_H!z^5$E]Aõf dS\2dz >ۇIHDZ3g*{i?L[0G.8eH$IW a< ;t`QEOpK|lKbk;dk;q;hzJCv'D&T+JLrv:^!f$ WeCwQ(X+T͸eI R ڮ  K+:0{|P5ֻ^+b3է zr:4,LeR+dRBZzD'h*89&($E%?fUws+Hұ(H̬gYgKb$#jvѻRJ7/!]9!eׅ{l,7{K<>1=\EJHKHKhZK a$:JsL4zĸ7<,*ʕ"Fāa a'<ʺ,[.˾<\|Ȝʼ<\|؜ڼ<\|Η~ (\!K9w --t:I0)EdzQ[B7mKݳ#Hb}S5;f~]T]a$LCm\m^=l;o|zj[]d}c"Fv]r۫7C$L-B?R3EiPYq-t!o}Fwy:(uMl)0+i7$q`mښ! a#!z$R,&LRUz/:Ğm:OlZɴ"PIV q:H)Q ë p`mha7쨨rJ|VZͻ,+s.}r}۰Qf&v)@љk=]Ѷ'Z3%EyJU5q &xf3;4{$ɷSAVbKqI6pk9W1ÁLo׉'VbԹ%A~n҆q:/̬Lm_ *5W0<1K3ჶ?WyE$- "bB!JA'NDPwʲ֔|1:b$`+NAj4HF^*zbK(VY-ƾ=0VN11dA}֎ٮ&>S0h~[0c 3N6Imi?6nm+FMSixBoyxj'U4aAS`~mlaaBq\=Ks:l:bDVP"DV8feEqZ1 ѹkn/?>1~bL3 ifǢɾq?LbJ!QxMps ^n\Q!qF:mp3ƌ*wV  <(#'6n.tT E\dFHRF!11b?=iP,h/P%QD-^$#A=RH%MDRJ-]Ib #e%MA DL? 84@͡lړg, @AívVXe͞EVZo{0k\uśW^}ɂ [uTO UR}4,)rbC#sZhҥMFZ/[[lڵ_N!N,q3ġu=~pGV,8Qw_Ǟ]vݽ^x͏ r)44~.W/Y?0@$@D0AdA0B 'B /0C 7C?1DG$DOD1EWdE_1FgFo1GwG2H!$H#D2I%dI'2J)J+2K-K/3L1$L3D3M5dM7߄3N9礳N;3O=O?4PA%P^kE?C-q(CC4h4t"#&FRN Z,-UZJTH UHׄ=iHA6ZJѨ%ݖUY[)Bt(uY} ]nV'VV:ԇHHߎ%x~SVV ZV`x#D뙋 l9S7$eo EXrݓJJ !uQ.(cd١ 2$l7dWxYncGdEbz-$#gh%lIu)"I9! Qwك! cd=*:m\yU-Oÿf?y/XI)жq).Xų8hBRZtJa ٽRs4 (χ'0HXtbVMJ : |f)"BXcp&h|OxO[0$ǽqI@ڃJ~PSy{jQ C"Id4aT۱שiז"XB@}Ѕ r_cFÖ9SAL=F'!F w6G D&(*1g4󷗬56dNB1CC2F5ьgDc?gı*A(юwcx7®6`c i%Ґ<˨$Qg""%9IJVҒ$xxINe(ɓtqWE<]O L,CVRWQ^Bҗf0int#4@qÄfdY  f6aM'>̅Ӝ9չNvMsUOviwӞg'OO~ӟ|a::PԠEhBP6ԡhD%:QVԢhF5QvԣiHE:RԤ'EiJURԥ/iLe:SԦ7iNuSԧtO:T'4jRy)թE.:,jVoĭ(ʄZk0չyaE+'|a/oPǜwP/4կTP5OIհAT'+ EOY$c5DMs-coR7EmjADmle&x%7M$BE32ghLew1e[15re8unt\2ͮtk6׼=r;^U.x7 *;;[F`Tߵw Sb`(XeqOXưY(\ g.OjGD&@rb*v1aiAr fdy-F*A,V" XrK( 0X,@,1)3l3G$l^sA|9{9v3 =3,?K,(lC>l F({+@x6tlǜ_-P.jf(Kj2Lt%˗.riDSzA!rH JV_]߀HSmAW;!.3bcnvC oc.wMwk{7Doyn КDد)xB,Qf0P̞8~a 7jr\'qX"F7.9Ȝ ,WƗ€UfBS|柭LI.DK=#&wYNZ7Z^v6 --\Q['[w/tK}8xd|us (oGHPp7$8 tͻA9.z{xOycDg}Ͼ k/y=m{S5 }gi1}Df/L΄P_}] 'bK%g\@˷O#64cÿ&Br+Kۼݳ<[@<J<t@6H@@|4\AlAl<3|AAAk` ,%T?[ &t(&(B)B@+ˆ-@-=*BC,gbtCıy:d`!/3'DrF˶h|/I(-ʫ<; - -#! ;ˑ- D$w6$Q@I@Hp<Ip6p 䀴]S=nsE<9cH#1=mlu8Bn1< AT:} Yp "AkgeO  |ͷ 5a3VŁ40F;gKa .@(Om`? ziP;_,Ibz'FDd 7$|/)24 tɇ+!sbz,UthʈR b\[ 09R:rAB˙ J)jR)6m2a&HY,pD!3Ykڥ!0I@г'=}@OBJP.4(D'P>F%QnԢ )HGQ~(=JERԤ.)L!ZMR%'uS> Nӡ=E*RԢ:MS*U*5X*UUN`jW*ֲj5a%VUfk5k[jTx+[zW5q< vgbAӱ5d;YV,e7kYbֳh?;ВM-a3ZжEmlUKemn][VmWlQT:}tKZؽvzxK񚷼=vKIԞ{Rʷ~ݛ_׾ 0_>0:y^R #L [3 {8% rGEpI|b+.1;cX4[8<L[|Xu&3N2,%'ƺgav_rCLO2cN3e5`vs,f:Yu~<~|>ЅhA7zю6 J?ғ&tPME]^RSUFSUԠ LY:Duu}Ǿ1\{5f ;ٸ6rjjSgvnsޖb`' Fs;^ oz˻ZpN߱&pgw8!>q+Ħwϖm^O9W['Y=HS7ssM?ymBG:я7}N:o,:ַs]/=Nv}fO;מqCowǽu;mjxx7񉇼#V=|7w;8 G?wjG=zҟo}aYm/w_6~M/|G~gr|,8%7'Mٶ}O{^w}:?~{?7+SDsO_ IOD7g(!O! ,D H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ 0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ_,УKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&@裑餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k f ,l' 7G,WlRw ,$l(D0,3T8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'zK*J Wngw砇.褗n騧N4rT:oMU-Y׼1z+||G/Wogw/o觯/o HL:'H q̠7X:q GH(L W0 gH8̡w@ YH"y&:P\$'E(X̢.z` H2hL6pH:x̣>@@! ,\H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞Mnͻ#N"ȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|g%)蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+knL+/k, ' 7G,WlgwE|,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|-n'7G.Wnp朏M\箵褗n騧>@nn/o'7G/Wogw/o觯/o M L:'H Z̠7z GH(L W0 gH8̡w@ 1H"HL&:PH*ZX̢.zO º! ,0H@"*,`Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ`[ NȅIμУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+/ko+l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗nSDꬷ.u$nh/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ :H"HL&:PH*ZX̢.z` H2hLc ! ,00H`H*\ȰÇ#JHŋ3jȱǏ CIr (S\ɲ˗0cʜI͛8sɳ'"> JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹Ϡ/ MӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8'G<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袼裐F*餔Vj饘f馜vu|*ꨤ*aꪬ꫰ƥ*무j뭸+k&6F+Vkfv+k覫+kml' 7G,Wl¦LL( 3Y@$g@hLB'0Fg! ,H*\ȰÇ#JHŋ3jȱǏ CII(t3I*_d͛8sɳϟ@ J#o$! ҧ 6,0DjU`׈_B [kYg,ګkӎVm]f5{Wn[{;wp`Npby}LƖ)vfLڌѥE̢S^ͺװc˞MLӶsͻqhX@%_tgϝ;سkߎ:uF8> V z˟O[7x_x'4߂ 6F(Vhfv (:F(bעO(4h8<(ы>dDiHwАHPF)TViXf\v`)dflp)ti2|%砄jPL袌j(hN4*餔GD@ hA~D.t)E)uNtj:ePŸQs+Jtj# JQh A.iMBqKBF+FGւg5>$Ԫ [ʸhoL6!+P-QKF,r+ i0HP;!KNl",04Lh\P ='DRIAkHk$pHEJa1ImXg L/=H ;ZpM!zAz]z}a-C-M'~ Q7dyGMgwHҚ/T kx.ICߎTw*Kzkz}O' M㫻?DJC}La,: =Bo>$[+ lnD篺EM G⾁L$+̠̍# "a6 CvE D_CBُ#CWgIآMF5"$F0P$Ҡb1n~: /!2$:*CBhg4 3E3*yOX-1G*~X B$6cNrCb"3-aX찎\L2LvDz$Cȋqe#$YN'rśT1' $@f~-&W+ 14$pc'ۮnő~U`ur37RVVIZsBL])MHlByOdJͭrΜy39lJ/t(6ERU ߲Bո,]z}Zk8 ]m+D$@䐄I-K࢘ctLHrKR-MzszD9]]e4ÖOFv޳a,D߆ H!2/w"Iv?N?E4@f S*ƲK9,Jv"H,2!Q@ 6 AHK:{(񍗾0$ "e\Wɹi lsGT/h9BkrkJb8 B7`}ȼVjŘ,ݓ$\A7Ej 'oϲ~oyalo~}C:n9'MnNv{~WCyw4~ѹNOrջ'N~SϸE{ GN|2'OW\{{ުW$~y:+R )e"t>Oҗ^>EP %f3BfY6yj;bs_Bezx![7] U.l=@s/2m3ڤo[&hAbTď uH $xSK|cG!,ԲG{)^MRe(.*_א Ȩz'`/y#+ 7o /D#х c]œz4kfg|> QVgqPSsDt;Wy|a+آ~SWַRKWDKy">ԑw2'RbH,|%煀Y QtxcO1kROq| b/Q}2+H@\F5"H7"13|'9A- tUMPtfaUU%sJ>豂W2 c!]&W[Z'XPh "!K8X L[%S !kxCY}0W{q*x,M`Oe1&b7GO_&(|=7 HRJ 8Br>WV^u}E_dm/S$a]u)r=vWؤSHƌ؏@FRmtRS/dS]ƖRYbRUb5U79x'Ƙ6 w?OF2tCIq]Dv3Jw(9$2ّ{"8<ٓv>B9DYFyHLٔNP즔RYVyXZvD\`b9dYfjlٖn`p9tYEwv));)(DZ6ZaK-s~l Ya$,>sAasddӨU$9ؐӄeD!b71a}9'zutAaVWł=U{(D$љS@n:D^F~H4JNPR8L>V~XZ{b\`b>d'"]hv6jAV%n~(^^m^f 9mt7>;KJu!91UN(~07APeV A&=[tf>Hug.ߒ hB pn&UT="[$fpF]0Y Ȟ9"a|:m+:^+^$א%1 +zJ~W}ﮱM: ( ?fɆOs\5WGnQ[L$o(gNQ1':!t;acSp*D$ݔ@!l"o)lFڝWɹF PAձZ^Tb?%Q| zٚBcCCyv!$=o"LC?faʅ%P8C]2a>+vcH!RGVSL5męSN=}TP48*¥&lPX5VQAiWXr6Z AWDśW^}XݑwPV$?Ydʕ-_ƜܻcFV^ϢCD֮稞O|}P`[kƝ[n޽}80oōG\r͝?牴 klsC>_Ovx͟G7xݿ_|gZ!kg'̿LS A8"B /0C 7иD$ɨDT$Ņ\0FgF;Fwq9fŞrP@$/a3Ȑln ' Є'2$f'D3M5ds?$ +Kl 4r/8MA-"B;) 8Z%PH̎,1L+EH X ƃ҃`$NҔMe1W35hCw@;պ Lj DEϕ\۩HҘd(b"@)4SIr 4Z*,WZb^LWBl!T1IW3q:8%]%)`2̍K rwqU T B})߂E("b6.܂Id7 ! 0geTSWp#.UܐϨx[pi۴DLjB zuEj%f{.xu= `E't ;o!"`e0[f( p7O7zh J6Ȼ ui J;݄?M ]$N h#9~& }[V |H8!2U#! 5 ªL,6,$c^w6t&&[r!KEA򶑽$} aDGHD0YV:H]s G)u$:\wAD,Ua8D4O4ȜBդC^20m~1ńȡau$DDD#eIl!F7&ISɱ"-" $1Lș+cFym%u q8x-Nhsѓ)'mT>QrDes( 'c#D` i/YeVK)Ēײ#  m;&Dhc %&2+VY]f6sFmvӛDg$Μ r7$:AG :I% .krUCy.iDD#rP6ԡh*tfx mFXV-e-+,r}WЈr5iNuS=-P꜋ըGEjRT6թOjTMrƨSjV-Pvի_kX:VeEkZպVխ:[:Wծwk^Wկ,XְElbX6ֱld5XVֲlf5YvֳmhJYіִMQZꅛZ;[HBSΚ%v F)CA-D,1 {C42.Wn:N )*mݚ^^|j׼J|K܎τ>b(ZFPĴr O\^ ŨF(灠O+R$}&p+cLeIb %'~HT4DOO`ɕU%&O2h83#AӴAHA>[h4IyV^#d^G"\d5rBbpBHXe:AaLW ɝb.*ٳ@_U!vɌGvcrZ!ɇ 80stad4uM% nt&2 HU;XHjrWFesBm탊 Ae/Ļ-Kr枈q3FO4L]p'Cx:8N-oG8Iь!$;  t>ܰ0Dþa A4C8888C ;1A ͓5C?d|ˋ7lJČyȈEN4Wu¨ CO4A@ R2$ h{ NDZ[2$X“n+@2)' 0) 02c/S2%SFTE6p4G`*0I ;:!-| Zi<{C{}tj?@A%B5CEDUEeFuGHIJKLMNOPQ%R5SETUUeUWE80SZUXeZ5U7X`U_V`V]5d_]bmce^EgUViVbVgVmVopq%r5sEtUuuwxM”z{|}:z؀؁%؂5؃E؄UXׅu؇؈؉؋،eW؎XIm؏ّ%ْ5ٓEZKٕeٖɐuٜ٘ٙٚU,Kڠڡ%ZA[ɦ-s.|kZ+(T٨e@ڿBڞڬگe[Ʊ(CƉ3۶u~ŷEvۺۻ;[+[螾M} ] U M ĭ\-\=\M\]܃\˵\ \ }\ԍ\՝\֭\l\\ҍ-]ڕI 7^) ޲^);ޒ#^eu^m^}^^^^[M^^^^%5u_ ``-`=`%`fv`n`~`` &6FVfv !&"6#F$V%f&v'()*+,-./01&263F4V5f6v789:;<=>?@A&B6CFDVEfFvGHIJKLMNOPQ&R6SFTVUfVvWXYZ[\]^_`a&b6cFdVeffvghijklmnopq&r6sFtVufvvwxyz{|}~&6FI߽_N_^_Nn___ÆƵ.>>_ŕNhvii0iAi 酘i! ,8HP I \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@T(ѣH*]ʴӧPJJիXjʵׯ`ÊK]˶۷pr$ ݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟O<(h& vD!D5hf> ($h(,0Ƹ]u(8<@)Di+وL6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤ"aꪬ꫰*무j뭋҈뮼6+z-&첇)FVkv+k覫+k,l' 7ì9;Wlg.]i|,$<%PQlyU*%0Ls8%r@ì ɼA'Ѷ1PG-RWmXgARE`-d}fl4n-t׭(,=v;n'7G.Wngw砇.褗n騧ꬷ.n sA o'7?{Wog/o觯>ӿ/on̘طBG['H Z M@ 68zQ HL0 p8̡w@ H"HL&:PH*ZX̢.z` ǘhL̨6pt H:x#T>*k~ IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZZ l s^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:vg~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PʜZ! ,sgH`A*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8cs@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ2z 묉"뮼+k&6F+Vkfv+k覫+k $iKp*+z&P  ` #mMRTc뚰#POl@QM5D B0@ ri,oLD$f- 1P 4RsCr4f l}&#ՠqpd,c  =RCl,itlj09)Ƙf@! ,uiH`A*\ȰÇ#JHŋ3jȱǏ CIɓ(/Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*m T;"뮼+k&6F+Vkfv+k覫+kq,pT2f$%P@ @%D beú @ Ie,>PP`,+B91 mP#@ "'t2eC%6,E,P]& Hf$!3t aPIGp+|f ,`PM 6@Lg uimf &H̳oI e@! ,aIH*\ȰÇ#JHŋ3jȱǏ CIɂN\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤ#j ! ,c H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIK6sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨA$! ,aIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*E@s!`S! ,8HP A \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@$ ѣH*]ʴӧPJJիXjʵׯ`ÊK6۠]˶۷pr<(ݻx˷߿ l,È+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ'h& 6 ptE!_>fv(P]$h(,0(4S6<@)DiH&9J6PF)TViB\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤj*Z©z)*무j뭸뮼ƚj+} k**J$6kVk-P^FFd8v+k覫+k,l' 7G,G -OڦP ,nluSX(eI8<<ǧ_%\6GЕM1THthD}"d\w^-6ᖽ-=y* l-t|]AE[e߄n'7G.%1.gZ#7Mꬷ.n/o'7G/Wogw=/.)rLXx/O H|L: Z̠m|GH`"< Wʍr$ Sp8IOB`@ H"qL:lJr#=C'ZzM.z|b2hL6pH:x̣> IBL"F:W#IJZRo&7N慌 (GIRR;*U򕰌,gIZ̥.w^ 0IbL2f:$e@MA׌D5)S$1 gM K7NCD]v~tL@b 1j /r $.&@A5Z,j1I5 p~+YjPà'-KӘe)MwӞF.> PJԢHMRԦ:PTJժZXͪVծz` XJֲhMZֶp\J׺xͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺlgKͭnw pc! ,8HP I \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@T(ѣH*]ʴӧPJJիXjʵׯ`ÊK]˶۷pr$ ݻx˷߿ |,È+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿD'h& 6oIa_BfvXCt($h(,0(4Va8<@)DiHftcL6PF)TViR^\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤj*RY⩬ji *무j뭸뮼Κj+} khK6lcK>+Bfe*v+k覫+k,l' 7G,e PqmmOҮZ lDt!@[X'eI8\%PuszUew ]BK]3&&l2`Grd]h/Jcvktm7Cs|߀ߙ7RnיvGWn_w:Dn郕.꨷.n/o'7G/Wogw=o2ErןO~ H: $@7z 3ph%< Wb΅0 gh: zp4,T #h>T҄ HL5P(C I)"*[Em` H226pH:x̣> IBL"F:򑐌$'II U91Nzd(GIR~%L*WVUO|,!5Y̥.w^ 0IbL2f:Ќ4IjZ̦6mCXmS4$7'q&4'dʩ !lg)͠'>1}fF=-o2dJիXjʵׯ`Ê !Q$ Ht-+6|+RS!**]{.,ۗO+xb2 91eĕ%F2g/wΌҟn tꆫI-5kաeF}vnӻi=ܶߍWn\UCMԖ1 =a^,a\zS˾˟Ov^ VD{-U&aGggfvImIUӈHxلS48P0bӌ;)XR(.]#A@>$EGV$EKNDiP=AFJrɤN RDeCX)k2RYg24xU=@YiVHfMSvF 餒VJ饖f馚v駞 ꨢJꩦꪪꫮ 묲J뭶뮺믩fyy]oO[HDMJk^N- vVK㚛涫 [[˯Ko.o+p C|o<s r_r,$S̲wr(KRJ73y/s>,4D4GC' 4ECm4NtTK5\G=rSA~hhlQٴō]7ݼݭw|7?ExQ%'7Δ\;ocQSc~-y蟗oN_QgFns'[4cn']N =yRDBB/ {?[*2<Ď~L@rc]" B $ v "("F!G Ⱦ:PxmT̢.zQ!H2hw`w4p9x̣Df  谑L"IFȹd~d$'IJ̤&7ݬ(GIRr(4*WVd +gIZ9Ё-w^Ҋ0ILRY1L[:b3Ijz̦6O 8YqDRtL6IzSCl=~瘩@JP SMB\DJYN%h93юsGGJqγ(M)7M(LgJSa5ͩNYyҝd)NJԢ.rFMRo.PcOJժ^ҴV@r`m3cհ*ZVy3m\sԹkk*\׾M~ `#PWreNud'+#RZz- gGKZ<2e-j= պVkgZCM p1q+rXθ̍W[+ꮵͮ]uxJW=zwz{k*M/~[S^LڷN[c?R [xߩ {x)`0WW0'cLcf:3᭱{ؕyl!L3M,lSʓ1BZ`rdLfDhc@:pL7Zx">~%,Bk̆NXOGVE;~b>O37V35ӠVCMԨSjĕŭ5"-ZgҼ]zw}6Z95 "3Z3]cSLQ6^s 6 rwNvgؙky%}{p9 A7;|EjKO xqc gn8?$wO.gs56sn-[9Б{Fwo3Ezw.ujԜ?}%z&;eÙsg}=ˌ+Ɨj3~w/6Os~1/ <#O&v/{d?Lb~~ez緀lv}l|} xahdׁB{ h}8WƁ&cGf,x%L2cX#v}8eF>;D(gwO6Jxd7؄Pd=8VC'f\h1"ƄbHcOX&hcU݅n_+tamxz8_g؇~?x\|x5_v؈ f[8ohk`otw׉i&!w6`nmd?x_RZxň(]^X(YWθ^jxEڸ\؍^8]Ә㥌W]X8W񸏔uXՏXܸu9\_Ռ[y[ّZ W 9&ZKe,9Z$))h'hg:Y%ٓu@)Y+9?%FV1EǔuP)XH9!iVXBQ)\9YUPbI^e\RrlY_Xlr kYfxɏ踗M~U`XmiTtlEPzJٗ Vxy9ٙ\ T[9řY_TE_N}9S9䖶`qEB@iGhS'9Iiy4̹8Ewvgih'@L!iFyS4{6<9ƃO$WGٟ҉Ge!Usz;D٠x6ffZmig ڡIw"!ӹEzmd,l^YRrϢv£u @J9z;=?ʤAFRSڤUWTVʥXZڥbcdzf6 km oZqsEZxzzʦ{} *Jj|z~œ1JR:QYjR_MImZIjI` m` 0)6 Im UmЪmાjL$zPnP a  ت4 % 6"đP nګj Yƙ k W8p@J:d j}* p Q mp q ˲n H 1 0 :Qk:д?*FH!!R+;Hbnrꚯ,[%˫n@zlɪ> ڪY%gM + U;kg P9Qn;:%uk脴tkItu: EPYʻ`e h*iJ;˼뼻 +Kkۋݫ˽Lͻ#d+,黾۾;[{ۿ)9(( <\| <"\$|&(*,.0<2\4|6'%%=&?<&fR&:Aɹ%A&I%K&Ml(O&mCUWY [,El_aceG,iLklm~qsu ǝ#Q1r},ȁlȅ<z<\| ȕLȗșȇɉɛ ʋȡLDɥLUʰ˲<˴\˶,E|˺O%i˽\,<\ L|͜,<\L|݌̻\;Ji#{<΢\GGeU ʒ!!Q(a 1aRA;;Xh'r꺰,+ 0Q<(<-|+TqmpI`&ͬ=}*},] Ѭ !W*:Qv&Mb<!c{Ip۸+M|=j dssRŶګĊ*"=}- }46fKA9׵፫㭮L =F*ͮ<[ q0؜m=-F}- mZJݷAռZ ?! .`a@m i3nz9HڼAn9 }@$Ϋ<^M}(;­]nqSжQKmm ݯQJd+EUQ I)!ݎѫ=Q\$}>  MOͳ mXڔ16Bn ! ญ;ʬ!u!  ެ{ p?-U=aFТ#"tޞZL/5I"N(o9i_n }ң{;o/&O!H-_-!"DJ? oQ(>@A_;OBDHId^2 ?R_ o Z\O[}V)o'o(?hglOr_n_qOw+$5,?_͊O-ᖿLO! ,H*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@(ѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ\ӮD/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(Lapą*LL H06,A C` ?!Az!G,"a@@ND N@D$QPPQ@>@P٢hE ՀYFVG5Q/!8cAE)z#BȻ w FE:BiI(%(@zWCv.@=@yʽ4q Ex;2/ILe*oOĉn)y \neL:v@ ͳчf9jz=8IrL'DJ$D" m! ENumw-5LϏlQ [ LGjЇ*(Ip-6D a=Rɍ"/NHOI t)5K̰ICY*[B* ۰O5"#%iN݀ $@6fԓ̰'S=rD$V"AIb 6#.-)Q @M(Yo dvO[ I$ԝ ^iˆ t/3C[hò-(RjC J7A !!?[o MPΗ`t*k3++LoY2ϑqMr:ЍtKZͮvz xK;-z|Kl @@! ,HP*<‡ BX!A%&Ę1Ǝ9r9P$ɇ&O~\Ɩ_4(fʙ4YμfK6}SICDzT)HKvTjFSʵׯ`ÊKٳhӪ]˶۷pʝK.\ nՋL,I†I"xo^Ə'L 1N#_֜y2dϒ~ 厘aYuk֠7LZvmڦG.Xwo Nȓ+_μУKNسkνËOӫ_Ͼ˟O_a(h& 6F(VhaPfv ($h(,0(4h<@)DiH&L6TViXf\fH@`)di&:l#ti}|e+~*I衈&袌6裐F*餔Vj饘f馜v)_~*ꨤj'꫰*무j뭸뮼+k&6F+ҹgfv+^K覫K:KWl' 7G,!gw ,$lo ,W,4lq8<@-DF'L7PG-TWmXgHo`-dmhlp-tmx|=]~.o@'~V7G.Wn9t\^+r砇b梗nZꬷʮ.X;rF/o'7G/Wogw/o?wﷳSʯr\8|!]x%M ޢρ0(f9q~SP@C΁e#xBD'JъͨFOn'HGJҒ~nհz xMz=}|Kͯ~LNZB<5 nR!bAZBDpR$A0#+!#΅0Xω[ dUujF\Frz*l4$$=rUJ!I@*KYFEϬ"/lL:xγ1=π.a $JP9 JB$$ h1C[BNH E#!}4r Z!J$n$Z!E ~^,6 ԰6NcmH I6F$/ ІKpj`G;.! $@ٮ@I=lsCS]ыl `},:n]h$Ht|6R @mK9u Vm 7+7ݐB3ha 68K`pIhᒐρ$8.7zyHhn2NhOpNxϻsu\;8&^`:;;3?$ۖ3T9HAÓD2==gO{5ϽwOO;ЏO[Ͼ{OOƫ$ ! ,H H(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ J!ѣH*]ʴӧPJJիXjʵWKٳhӒt۷pʝKiX˷߿ LÈ+9xǐ#Kr!˘3k̹ϠCMӨS^ͺװc˞M7ͻ @ē+_<УKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Ffv ($D'"C0(B^4<@rgcDiH&LriC6)TViXf\v嗣E dJ%fhlVdp)Ix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨag꫰*무j뭸뮼j&c&xF+PNkfvwKdG疫ҨM+Ւd˼/zC\l0|*6 7 Wlgw ,gl(,0,4l8<@-.O%ϖ4L7Y›)L}]O lEPz_[-.&bݒj=5g=%P-M;UxM`xvӻ=7N';6c%W_$Yfw/$}觯췏/oyi HLj'H Z̠7z GH(L W Gd8 'KbR HLD%:񉅡!k)ZX^.z` Ǹ/đhL6񍯒"H:x# IBb0{L"FD\$D/7Q¤#M+_X&xR58& ,mXrǕNѤ-wY"D7/]F9H0Lb:e|$2mp|6nz~dM6IrT8v~ @JЂ^MBІ:)$D'JъZ'`4юh`(K8*(MJWҖ=$}LgJe&NwӞ@ PJԢHMRԦ:Pjb*ժZXͪVծ ӫ` XJֲ5zY˵f Dp\JWRxkꊠݴU}e%+Mbz:d'Kʺl$h6 A"$@SJ@$A ƆHplkV$%h$"Wmpܐo  eR$_hZ6#fpY[ֳmx$,Im"6TW.5HmC`6" $D;BIh $%!N8-%H -|X{F R,7 e-n[$-b*%j$Erk+&,.r["! :exߥ ֒Zn$m,$ MR;>3~MBЈNtRfE;ѐ'MJ[Ҙδ7N{ӒB$GMRsͦNu Veg=>V:&w}F+MbNf;ЎMj[ζn{MrNvMzηݧ֏! ,H*\ȰÇ!Hŋ3jȱǏ CIɓ(S\d0cʜI͛8sɳgG> JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix橧|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k B6F $Vku\iv+k覫kPk/bݫF10li l' 7G,Wlgw ,$l(,0,4l8<@-DmtL7PG-TWmXg\w`-dmhlp-tmx|߀.n'+GdҒWngw砇.褗n騧ꬷ.n/o' yG/Wogw/o觯/o?oHL:'H ZdQ2% AAlP$ l! $"$ "H& md -@z% i& 2G(Z"e3z_X(5>6n!/ x( r#2-:AHv$%h-9 _FhI$AmE?@!Fd$,jI% mцр/$ #aD $!  [$AA^x$ dH $Hl 5IrL:v ~jC@! ,H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ ѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k dԾ,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhV $$ F!@ M`Ka$m m8o"P D Hw m8o$ @mg `-Db8:GlK6v T wHz{Nۢg.@XNGr@FMlޑR8}>r--6Z9᭍-@o`7"D¶ v`_rN# J=Õ <dg7•w m l#Ֆ(+ ! ,H*\ȰÇ#Jm-~ E'h&8T 6F(!nLhfv ($h( ,(4h8;, KnpS!x7bFB,͠fc$:PH*ZX̢.z` H2hL6p#r(:Et> IBL"F:򑐌$'IJZ̤&7NzAG'JPq OV({%rL(K5[ 1Ұ]2F /Y7Z&A&19Uƙ&3IjZ̦6nz 8IrL:v~ @JЂMBІ:DV' ͨF7юz HGJҒϘ&MJWҖ0LgJӚg%@M'C U"SNR$$ I@B "qˡZI$% 7H@pTiթnH$FDlq `6 H&A򊐩i\"TG&y^ǓӋoEN'+$`/RT 5 ڠٌ<`Hye vCZdIMLIi ՋJ5|VU ["`HoS?ALYj%Qmq +ޅV"5Ha-rTZ6x, z U ݮB[%>zZ6"Fr 'UH0kAbX6JCz;UOIxlt`W"Z_R_kXӚԵ\z־{l^v5e;ЎMj[ζn{]#Fwmjv;^jzݣwo{{z 7 ޷#|'3Ns9E񑛼(Ur.eۼ8us>΅H'r;PԧN[XϺַ{`uNhOpNxϻ// G7~<'/xW|)qw|9|Qzѯ'}7OWm{~=G~7ߧO[ϾwsO?^~ w'z/jXoxoxX !؁HtJt&Gq 81 ؂4(~<BD8FXHxJLN؄PփTX⦃V}\ ]^`8bXdxfhj؆l8 Zhe0sZthrwuh}|l%xXxoo膐8&U( ؉8Xx؊8XkXwO؋Q88f0SxȌv(Шz8UҘظhƆkH8X(& hXk8X5868qڐ Yم )I uؑȑ鑆( 9!y&%$ɐ13 y24ɓ6鐙؏78BYȎo'(F8Ah?aX\U^)T9^)hjlٖnpfYtydisuwɗy{G8jL ;X攆hwYI~Həؙ虡 i0.nɚٚAɘ񇘫y_wY7vɌiyʉٜɜҙ\ٛyLH~k2/Yp.jHjnvwkF9IGy9z=:?)Ji, $*:ZY j# ohɢ01ꢗ' gZ&cx Tw]Ϧ \%cLH1\&Yf ѝg\]ʥb:dZfjkʛܩGZЕ`Tw SmHp b'v ~"xVxEk h Ueښd 9*jz٪{:JGFfYkd_f_Uߩj.G:jHkÚZUu vlm*Ny9銮ɮڮ6FZjƫ}Z8V\IjF/ \' HPj~bIb9& m%-J3j2v Z"ʲ99/ˡ0k >[  XH{? *78Z3V?O۲,K%`{ߖZnpcYfׯ6vY6&; &YSSuzٸ鸂k5JE:|]Z kaz&?"k5e01&;<빐˸;c&ꄜ+H kjzoj)v;mTWbwWFz` kjQ!kyb떽h+j+v +ΫuwV R Ƞz8&nP %hIC[$ɪZjښ\r ;:(H/v/6'VG&Y~JV_j & k*I5L5w. uڛMd5Xi*c/ `/Rxp'!& p|kpAXƆΒ[IţIU7z7_z~a"ͷڀmb+86i˘rЬ%5G>PKFSs5F77)Ykc岔`M腍if:馗VTNM]:eH ~>+Cm {n;o-[[ P\D{{l|lgzۇ񮣧uRJ/HV&@j4`S3f`x;!S\2'Y Gx2m|+Afh0Ё>,(9ʓ$5~OָϊUE.vы&8F1"ȋbxƖ诉kcY 'xw]XFu$n7HAP!yё{|#&GJBQXhC8d89JZMO]dDeS97ϕ{e+a9KY֒||[*u9FqJ%0Ka &2Lf.әl&4LiV&6Mnnӛ&8Mc$O$/ =YD pID~ӟFg0zf?R"ЈBTvP& "GCPʓ#(JKZ%&L4-)MnDJf/ը_-p6թO- XH) ܈M))^9;VլgEk] Ŧw6˩8U)5ALk`;ZnU\-/&}, 'eEٟaUih9;ZВ=f1[Z֦VkֶMm&ݚHA;]]EE$\q\b*6f԰#qXrjQfթ@pYEoz{HzOu+ЪJEWro,]~(QOۙ=RۢªZ1/,8'ib-mqY<eP~_ZtR4叅3o|;딡Kio&6SCnG|"p-j@R:`gm!Hd?qayAN!^=u|g rߟ*OoՕtcJ?l $,gLmAïF˹LHAm˷t˾4Bl.A$L;KːG(BG)ܐ +;؆L1 #9C@90k9D×;\l0 F/ٔMKl{3 ?NSpP: ;0spScNI+;(Ȫ ́5$` [3CF{f@dKuRQKADм{a":(uGxG¸\|LhQƋ=҆-t_Iź0''=! )ӶBKtUIZ>NJ(,{Pɂ;#˂xóL%ÛsîFٰP< $KH+=%Js  'GJʏxQ;kY$K2KRm<O Sb*SX-f㐍K  MNC)Ɗ$Q=)V5#` Fθ ^: c_ZdMA`6EVFFd8fG7z5=!:̔<On+4fSFeCQ*9FKNclz@^Ҏe&>WMcFfUdMނLt)$8<=n Q3(Ms^.,uU%җI(HWtRƒV0V/$\yMŕMVhGnhI>䇖Dhٺ܍Wڨ !eW,zSdefHi+=X^#SF&n6fgUWH|k[CLh`nޒ(j8zbjHޮ~dʗNh>XTxNiij}3gkQv~_v]҄d犐 _mzj!cuM *>Bvhϖgю腁܎Fzs1^miV)iջvjm~%Vn*Unu-Ω©|RXb ^2^{mS-\.\9@ogm-J4kSվ2KۣMGMiUq{^}ԃbjAkιԯkk1nUma1$l&_`mVrW:lj{ow:NjZ&L."h{ $OhHN4n%m+-GW0 2,(9]hWkmFN` mT%u9]Μ3Bܦ"ϺRӪ qI@I%q8R eT}6sWE O8eSȿO%SXwYjj ֕YnVtRmc2^&;c樺v4pKt۹6:"_&r ,|;JS:ap7;~mBZpQ)ksMpqFvCop]Lx4h8{Ѓ!:3KkYKLr˼$$όR8Sڻ lDMSÞ[;*Edlug66dbfg&,հq F7/RQ*.ID4=?wT4R k>N 3@`xoNi>ʈhw.=w{/jYo,HP PXЖm!TVE$)$ JBM#E! `/gҬi󟿎7wmӠ w޲PJZէYNukԮak֖ʝZ-,ܶqkܡ?km_mƠ,6h6̚3s Hn,@[F!z3fgcfmMjssڟoڴ/nh䉏<7:OrzH% IrRw [ +-IZC~ ))^m r!ۘ$hD7FIXAy2 qOD'I<=%Ɇ:%"#x)"-.w0(9c=裎3!QIO K'dmSc O'I"!IJwҙTC &9'HY mltz !)RТ 5ѣ):):j)Qz.$AI,`v%(x R2)AFMtZ qh{4+AcfhJiS:+-V+)RJ(-^+췠NkcGEb.UZc/[/oz>/*\0 +nCd `9gT:饓 mG~%iXGSZ< gI縖]n|񽃙j<[?:4QtArJa`n@7~ӂ z*kHA`iux)qrDcwQe -!]4))A0$*(.&\ўHNXP7mo(&e!Z "fF"̓˞b@'Dk1vl(!m0[`s&=lsIMn$HNܑ?cM1$ :D>qa$Ir&d$&!sGp1$TLN3ɨHL1m'uB$[C7>d=ٝ;8I;-%.$'I>AK£2M29O &Ӏkf3iQsOd9@3)P\MƫhD\rKuM5DBzE<}Nh3ȧ *ha(Fqn(G LCAzQo~4b,}Je?JۄKcӛQ%89GH11"  cTQ11Cь3,KkN? -"9c4z&AV?& L:Gwמ4\PZ!8\!A 9*vT<]oPo|J:M#:`7 )sIzR8j*=i3hբB6 '[e9snvyr-ou{卩4 Q ӵ̹P!%*v>~޵'>5[~-9@1-/I$Ė4 m1cK&@d<*X@C0 *NdhKّ1Ua` #~%f5X>Oes_S 5*w&n%,\RȟpC"ɧݭ4aByw%qPxt\:j}oSTK1&1:abL@i )T2ZCI\-(*ԯHZyQbWu71Sc鵀cKsZ̞r]1-Nzԟnn[hP7HئOR:ִqjج5*c}mYRl^v$|C*H@6 d@ )n 6tu$qq¬R#{#Ig'ِdA{r,pF*gl8ăe p Y&J3;z}n>ϼ9R&B_dxu Ń.ݓ k`3=)B, wxSm N_X㆟ P'ܳ9I\lGBp rTw|I8^/< Gʌ_<oMfS9E5 WM0@г}ﻳYK ; 1ARbF ƫJq^!ϡFWJJu_IԷwkg|kO`_l5/KTELa[SؕXa[bۈ ݰ۸d݋[Jھ} ~Lȏ=ҿ)DVLzO% ֠ n p.on  as`hDAQȹuɩ7?yYŜyR^?Ez%PRT)K8{5͐ޠ! : "-!bI^ǟA HqXGT ` ` ,ّ[b]|@AXȢem -cIGLbbTC͒`  ؂ ^ >#4FcAilT#5"R AƆz-MQוSG E\b:%,BfSh@"RGa,PTa=YԿSԑt ?vč#I_H$J$H_K8KʤKd%[NڌNNOeJd!!G$H@@ ܂%db!l l PSr ɘ`LQXSIȁ6OW*کQHe 1C -#!13Jc>&dFdBRp#_LHWcE$șSy=#—P IA¦ 7H!==c܁%\ա_!!qd^'vfvfgeք--lT8XsViV]aa9b lB硏zQHfa%|NIA'@I`e% v`]%M^q4vh&5a.^%1avV=j '<~<'>~!y%b49whSJ } BF%Q[*qHG֤?h -nꞮFB.M$PPnP.ZM?Xzb bʎ[([_*.Feƛi%8ݙV|~jj"IV0fr-:~-)D*aKmm=]!ɝ,ߺ"&\z) ?Le.Xpz:0& 0BD,u0Y#grǞȶgz]{֧тکfk[U/i&lТ,کzӊ)"%L em/1 {z!r,ꍲ.1n$Q7|Dd܎+z㑊ޮk EAA8|G$Q\MSYbBlX 9@t׈uh)ʭw@DEБEFt.8.#n39/:鞳:K nP3P._=LO_ UPk{b&.Il'ICx1q%-o%'" zjp;%)4K#.v1j3q C]R:EPJs-``R`TR15cv2G03k ^Wp)C?e|f:ŁJ_·@0 I-"w ,XI!ABa(ltvkQd ud[!j,g'U# V҇eVf%mlC12miMJ_QbxYɚXMefJ%vL6VViǭHmZ$1F(e+]Ғew r52xS#"WM>R9?:S5 -(O'-kEhCB!\3J{ v.O.)m&^#j<*3[}t$PWnzKtLe;_;YQn>{ UҕԔPB-m nOs4sՌ8??3湬F Ņ$tBj3t_*|B7l`V]Cӈ%[9JL>4xhx &tsO]y4LXw`1RҠV $ jjxm%H@1\yPz3pK0p.piu`E0~gȭ+ڤ DXû Tu9@&kfoR ׷7$f\D vdC$U$F)c8sam<bwl!ott$|-Q}yWX$:aG܁# 61s?h|Ravv67fFlz:1KOJS"(.?/. R|OoR؇nwhvK ;IPk}슭C.O}tQ| No7*^"3S%ZDRA'D_Gmkqcn 5tiӧQVukׯUKXl۷q֍$ RMN` %GOOI"wckنhuwwؽt٧ORŸ$%mD͓ ! (4h<7Z)+(  ³ZPD @/Ŭ*p)qɦv᫠[ Ut.E6,D H E+tE QØ@BlA&FBBX 4cПm8Sl LP[P#4CMC4EIct4GmTB)=Dj,G/]F?Q1%SSA-MΈh3Dh3ֆ rW~Ζp:$.Z/X`imZbqChuq$Wr$w `raD)z[PD`i_؉ڀP$ )&@{)'*,}w ޵8+MBߺɅnI/g0 #( $TLݟB[Y?5L"j-jZjZ묹k:.{k^+l^n;n~j%!k3/J(:ʹM{\6 ܰ#ʓpA?6rtN1 *= d'Lj ҁ [p+_IHLʒ2o,of$q Ph> _V}.)l2 TVS2?hCKd@iXLt+ 5Pt2g7af ~2s;*R! C;axC!p6Dp'BlA$D)BщQHB-nxc;K$.Qe qVچG=Q=2Q5,8[A.q[d%XIMqtRRVЭk#IA1FWL_>9I2E%,q/@mBETЄ$AA}̋~I/ZъKp^U:sC&5 |/ڤ_e(3H%.IA\$HgN4*Am"`ZB!ы4NiZ?Xe K T$/Nw:ňqcitG>%M ϵT䀴0[P)D 9BL=.)7 Sҕ'}#QU KN W)"&Sp]gWE^_Ź.}iWL QdBfK+GR+$#PC}MmhjLxs)ZmW|ְ -d52Y޲c  T$N+,fgϱ_"1Pg5>`Y)YVYed$ nbO9Hk%OrJ8/b/Y7'V4fmd9QWȹgI H˼мVNYV4OMpO=G1gCuo1itDž=>YudN;>D=uE}GW$$rYJ#`OIS𔄷W)+sҺXK^ @oa'ªMqږz_=c>PݳCʂRޔ[u^7 _Xl"~ae?eJuKepK/7igz&ꎯ P6$ai0x,ǘ ʹ̥,d*w+M\'::DԖ'\0(y,P$MIއ^0&O(~ FOm'+B/+@(xLOw2CHP MLJ  0 ۰ P 0P 3 hݖHQ p/N-"&.|",N LfZR`Z..,I21V,xI$ʢ.\BeT<&O:0"1HqcqgQzđjEjopxG =/ i&N+r!!k RE$2"-\!Q&㶏DX(BB /NЯ,/$$ϾEf.$`T C)2*1**rtQL2R M--RG OE,yr-DBPmDy00DC ?/'K`C$*8VOt2I+0,p4MS5Q44[5_s56a-Q7Ц7s738b6i34A:2>-Do:8q$e`H,b6BP1ab_hQ&B':G;**0@4A?B)tBr99P qDʑ쒫* QOHFP\T0YqC&nKR԰&tII"4/J2tՅC,s$ϯb'?1V.'HXrLafR8n%b>&D(i$AIQU*Qu4!t|0hS~3{@Ӳ XM'.A'/ GR-&GDT 'UC n9V 9Ru6Z5[S9u[o[\5U[1)SU6U},fhr^",R82,:&L!@_a1؂= Td4i=1^"=V=>U,b`6+j̼%B")%dMdQd5@A%vTWIfPF+Cl.G{INVd7Un$i}IzV yHeSk6lV@@"f"+Tί NR!n+i'+FNQN}^MTT~dX_xbOd"+(1ddǶs=sAWƸ[\e')*dc`ux{5.E,FDGk{/G0'.GK xЬu;,-Dk D[M{5{|W|||u|w}؊eJw7S~߈}Wll !m=5n51&% &^kb'~=!`˓>1b{c?8o s0tYB/Bhb8gCK%PWIjm1g{f1EEdhgEJ7E$G_iʪxfEtJ4DkM؅xJݸЭ"؍3҇%#mume,^QK$/v'7& NIf=_Q&NN)'S'Q.DK/^M (B6Q9=0+YYr*[sz]#W!TW.TiCuvTQEҒ/yWh0  m4,szz3twչםw ~77~e:wb"Ju`%v9Q'L`A1;="k&%``,a/aâ^GT@3 Tm_/a9ͨQ}= :# BLmFxDvvxDxD8]TDުVZi=fh[` $!q7ضXn7ɸ+!ldYn r n/bMfo oG"n4n?-OʪO+'Ru N4 [a{@@uF4ry aڀ->—qVWc7ilςY`w+V{Д6-]7.Q 2ڈCHҚ"m{{kY gOJn{}ሟ7}c8! "(8{Cbٓ0E8/-J;I\&# TϪҦ9¢i}9hyTx^(΢`0+ TB0>Dһ1- x |Fk.̍32Y>|]c ~;1`h~"R' i6rHp8H[ȹ)[̞%^+118 \gW0{uKlQc YOC_c @Zf"Ira!ap (b"$hb.eZ"6x#:ֈc;r_fatV$?I"XhuTAU1JIDvSB$h-I$֔fJgƘT&QEH&T vEzd~~e}4Gvt(GVDhR!6$(D gq&ꧡzjyF@ *\*@ Jj 6Q~멫*kz,qIJ* 쳴>뮨  )V4K.XW9*-2 Ltܲf9ƛS*-S ;ܰ?,1WJ?P "_2#g|r {p-/.aUD{q 3궥]AFY M^^!9WA/6XGB[q)DtyZ`O_ݳ>vj|ڐ(AzTS$Z %[r q^ t5Nxoo~8'@ 3_ t 77㪧gٝ_ %U%a G ADk_$TuS?}OO=zobo}Ӑ.Q gRJWTSOh\K 6EiLF2:}hEn}4HCH^ @p$a piS-[`pT&4 p<vjT{8d%hB2XfDiS!Osд3e.miG&Ӯ ${i@vQ1KO 63 3M`G>t\x~mk:׿1=[ךN!81bLOl|nOMn\QwO|V7 bf{d5U孡*< Op;T0'qc|?Ը?/KŊժܴ,}O7aβ[|" qCk htn~eT:\>ChJ?͋RnHW `VYue~߷/Nc}xwAFGwʹ{K3`Hȯ(?4/JE@ˇ~ݒWwj!>YV; ~zo.aUX /~}B[zo>Mi|OjÆ6.6 O3~iOG?mo8+d@eh:u 0pQpWI%R5Xq-zhxRU#%/Jp`u]t,tB-(5Ht37h+0=r%[jDzgUUuJVt/;l3}n'e}qBFQxwux6x:f89g(kvrhxTMy{8OF'ȇvzgeAi6O Q*HBgF}Vh||Ɗ؊UUMA#f4*Ulg~WH~짌~x(Hըȍȇ6lE>oz+!q" ! 'd,p#xq h+ȏh,hB&da{q rurQɃRH #%)&$I.KLxIMȒǒ3 5/Y(*(IE$A&i_b d@!d a91t}e8dlHgxwTنV"Uv!MhikɖmmMmPH:aԦ3F) (-pStHlj {,CTID疕i'pPtHP0׆~_j) 1hy^d9 B遖IiCRwv!ys%XvI? 6(J@'PـG"h(,9I멞)C^YVi:'ɟ9)<39JXyəAYD8j`WSdG+:-z1L!ix9;ʣ7AvE8& w,v0UQD)U*{*($wC= aʇ'eJ7ij98Ua:"Ϩza| ^֔a1bʨ؍6nk(*(%V }-ZOj 9JB+$gʪXꀦJ&H ʠZJZ4yMR:jQF vB_Y&ʕ/ʕ%z,J]Q^M8kyy y'z +z c'Gwr;F 9Ze') j( ~e*{_z7Ֆhjg?+D m-vKK`C5}*WXkY~`KNyKƱ9kgoJNcj˪Re;7!iT7U4DbIkY{A( + KfSJ빖[ڒKe06i{C[XHDWMPGAMe8û:+Û.*Ż_jKWex" @xk+ٛpKaAI6 # 8NT* +=+)\g6{sPsG+@ >˳B˦H" a jMKIwʵ[ U+Z;7w\MbKh\?dǽkKtɩ߰ sUҤJ,\zY*QLOܪZXlż)jںr oeڹVcƛ6[N?{"^[t,\wo +ꎈr:2`{LZKcɔ3z7J5l<|@$A*uKf7 ?Mb̓T?2de & ;>HmB$vf+L=8e$BqhR\p(_B$nWi[sǽ1;6n% mJ| sCP*SPLa%$L!]#M表\zoCaV0%i%ب>] cd;vcf!vB%Z+Xg Q,N@,L{Kׂ[׉ky{+ N rLpA+FZ#g6HΩth{LƍchCZY 10Fw%ڎB55JBy4zz!4AsHȬȮ*,8lՍʢm}ݥ<-P_a;Hw;B#M}=Va*]JmAZlos%m%97`"s;>@p@wR7Qج~[BTU^$[1.Bi߹0Z>1ELH)O/_oO|rm i*y[a'og{RP]Xd!A ? y 'zbB'U\.?ۀ? D,%bA |5[ l۴ `A .Tؐmm$AbEm%G8$BINd1eΤYM9uڔ`[Ht fnl5/()ZUYּejaŎ%[6lIJ; mGpdiR7m߽ۗ+@?7ۘ0MŽB" HIolx#[N\zh^ԩke͚6J3sܧM\Z#ߚxVΒRM}L{N47f h yկmuW@ o[Ϭ @GCPmJLi 4& +\Ж6J@d<JHB9J 3>[kmhFȍHl1GǘH$mJsplG,e+#J,2KL k22IJ6DsL7딓94O>O0mJO;ma?sM@TQFmtRI+tv2D/ FԿ2%4!QKMUlihQQ=)EsT&]#k#Rƛl>SggR5Z쨵ZlIН9V[pu\t;wr]x`Ӛ^|w_~W,C_ J2Fx cabUx ^洞w76Y9:*%WFdSfsiZkP9^ECδhv ];$~Jtmo!:m2F[Wi̮FHj$>ҍ⊺QnW_!|.So;s'sC=[[UO}L7[UY6t#1q$>1Ac e|7PGhZ>Do)G,J%MLW}׭B- _0]~]}6s6(zD2(%M"A-͍=֋Ƞ$* n)QZ7 Jq]B\!&0P ! j Ná(;m8zbx@s(!O>OML w"fƋLzxF4QIZ&/rQn Ȼi6T _nS9O) 1Jr3_qpIGqb;Ή+$=@求zBL\ITwT%YPRt]-S)URĥ+sdsv¼XEcST&-oKqc)÷E2j0"Sgbc8UmѠü-zR:2'9a$F1OGkg=i9hq>vFiʇov!#$+S(fܖĥZM>LBD$Ԃ>mHRS+eiv&0CdF9%܂~hԋ<TKe*r!SML^BӗYj.Uc3˲bMVշudk\zVUxk]TV%la KؿVelcXR+le-{YfvܫZ*ճkhzΖ}5mjYZVkk[-,ŭm7YW%pJUB5d.R{FWӥ.Skz݆nۥ)[]W]q[Ǟ׼VogEmk+׽|_woj3S aC"~}]fXp+/߈g4W!;bXynS]ئ}c y'=orTΈ/\;YPmRke*O.#-7QnYIXͶZTNUiUV Y{v*(*]/?\dKЋf -e5Z[;d]Lл=tCjӆ&<00q>&'C+%D7;{BR X 8t PC@C]$=^E`l^_`a̼_Cd|c,FdF_FbFe^  BpEh#Бu<;$bBYًZ4E+E~D:;0*I< ݋HڛܣHܻHH۳գH3=Hҫ==D(Pk ;3 >1Ps '*I;ncA*dJc?T*%mtP:%X @sԏL,늭T˦p??qH:Z(SGЌZx#7/h[8&TX|3\+GRBAx><;!!<τ I.f 8͊Covȭ8$?)sLSpzlDqLž6H XD /d iׄG1и8p ͿJC;I %G&$)Gʀ7?N͉0:AϪ :̰J(89>ɐl i#Ql |ʷ6RR <\tgQ!Q^A$R'u+/yRd*B9t3ܱ e2 ӢFZ*@SS5IrS?Y /?M .45>-TGL|TQ>SJĤ*+mRN3TO*U& +TRER2U~[K.}U"U&MSIU]}TUğ夹bGUe]VfEձD=LCkVkV?TT;,P TUU-Wr=SENUt}W'RW7cyaWUlW3m:1BdW]X3~mXX,Wu5uu׌mW5WxX؏ٍؒX;{؉]YmYRYYFYY'pؐYَEYڠY5YEڟ]ڣE6YZZZZZdڢ=YۤZ[mڲ]۴e[۳-Z[] [[[ \\-\=\M\]\m\}\ȍ\ɝ\ʭ\˽\\\\\ ]]-]=]M]]]m]}]؍]ٝ]ڭ]۽]]]]] ^^-^=^M^]^m^}^^^^^^^^ zm_%_1_E_P_e_p_u_ \M-`_&_E`=fn [. n_ `. >``n~%ۀ! ,H*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc!۸sͻ N\k5碓سkΝËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)dUD iH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjdꪒQ%*무j+CMܪ뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0 t2l8&4G]_HϵLHBPG-TWmXg\w`-dmhlp-tmx|߀.n'oFG.Wngw砇.褗n騧ꬷzFϲ:/o'7G/WogzOo}j觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2hL6pH:'㻣Gw} J4*u)$$ #"4I m)H@:,7Y:J3Hja R,_ӆ <&Z"q \fZІH9Mj,mp h,`i1d$$tk '9I MBŜMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0 Bcj^8u=sӞ PJԢHMRԦ:PT菃T#W-HV-Ձtu` XJֲhMZZip\J׺xͫ^*R uKMb+2dbL vͬf7z h vM] ڵ*lgKԦͭ@r p{w 6JԖ .t{fͮv;z xKޭMzRpb{KNtͯ~/uY07" IFwVA2pX [ΰ { GLY=W}g, +ƌ0w Ow1!Hl2|L*[Xβ.{`L2fLXθB!?N6M:N4wys7ss=7Aш-J[Ҙמi! ,TH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛ ɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXfZ%l"^)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8%0<EBmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.nFn IIF$Q Q|$8n ѥAC}mHbރOma~` 0~$oOQ HIM$IXlqOzm@B =)o|K->/h6\0`$ $_0 gH8̡w@ އH"Q8&:PH*ZX̢.z` H2hL6pH:x̣> IBL"F: I"$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.] 0IbL2f:Ќ4IjZ̦6nzl! ,VH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*-JbӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8L!5@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n;>DI$!Pz7ՆD@Ir-I$TI$|$x{$DB TwS@F˽{iOw_/P)-l! I$gwAHo#H׷5/x\o5rjGH(L W0 gH@ ɇH"HL&:PH*ZX̢.z` H2hL6pH:x̣> $x(BL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:ЄW! ,H*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l3d ܬ3R!@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褯Os ۠>:>< P|߸#1;XwD"IwO I췬>xP'PWEm$7$+47@ \ H`?I '(@$p m0"Ao] C-"ABll¼! @H #D@}xk rAPo$p*$j!$Dpv+ Av($ELx1(x̣> IBL"FbF! ,H*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+- oV+l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg=_ KB]5M M]`wcP7f}kw mpwu'nwy 7*/>.vCh.N8I^n?I}B>Ǿu B ;-P (^݆(﹇Mm,XD#qH"@,X upJ0; }h $ PI8N@ې ق7KЇ}l+yQM¤6[iBSD57\_DYгVӮ]r}b TB#E 9L"CE:|$'IJR\$R4Z8!(GݥLeL89<{(" Wz@%,UAe bKa:RPXXRB1^ Ȯr J&jr]ִ^΂m |L.%hPąDEY +gD J AI`˕` .BPjQuz:K984$'tQ'ut$ rQTғKj-ڐ?t*,KP+RG ?YMrџ04vhHБKH!b'eA,Wԏi);A:d+9*CMbQ5-2 "ղꋧIIR̅|FcugSD%bQFĖQV D9z;c3"O3^5) (A.$tHOIY f[iU>:&$G:Rגͮv.$(tnE^MӮp!}r4T܍|Kͯ~LNm;'L [p,{ GL(NW=TtX]#oc$Ǘi0-T;Gr6!DJ],*[Y(T>JB8WƊcN2s;>daLg[i'Y!΀x1ktVF хu1fi9†v9ritzӠ!LԕWAzu3kܵ~P}1oQH?C_|LDSzD~б0bY*FbfjVnH uvy!'O`#ԨFL Z޸ޓkmk58gȭuB."w7H)W :%x9\I<0Kbn:%< jޗ|I7e:_ 9FfD"I_GN^zFKEH\6~$H .DL2s68۟Rf!RRMNXf0xQj؆npr8tXvAixzĆ|8Xxy؈8Xx؉h(x=T$1+yamXj%idLqK8d xE̢ɈXfp`u(Iݳ[jxMaBh!z57莤N@sp%<ŽSrL+nB(]-±~zfpa:!wpv\? @~'b|yAD!L795)cKQq,ԂG7aOF>oW$MӲ#ڦ,[EXxᗞWp!&A| 9St~a㖉g'ђ3)l%iDeSʖљHxKZbSyȄuمŚ( >Åy9h,)᜚ia)&/3RٟBŸA9eo韣$5uAym{`) Jm1Wr¡ #qb1iА,*9$j5.I >61GIc0nՁW-@!:Q#DQZ9Y|VvdJV M'k.R7:~CpZ^x_aѧ%ښFM_D ZZƨ|Yv0:zتګº":zʺڬQm9IZ x*UjJqWt=*qҊ5:0 Zx7"x6"y &V55<&3v"S*@62GQR䇯$MI ~ o' H2 qBW?Ȋ85[tA;'054Q$9*'"!;ش5-5Y;TI*^"X@zW c*A/3"U39\*zACg1ǴAFQ}SGI"Q {K: 'Ga h@7IIrq+STݓi 1©BwHɜ9ۻIԪ+mةѹU$ŷZ6BֺZ1l˼ADٽ^A{pHt=۾R8:_['՛ۿK9R],Ȇ!+1[#"(6qjtœI!QQI ʣltɠ,@,`|l1tw.ˊH^X ) l1xD,< |؜ڬhL|jb?Crl<;YDl|0?Ra;ԑ;2rqG,t ZHP0dw.ZA DlZ+҇Ȃ+=A tb dyQwf:#6]MB=3LMO i\R҉\}a`b=d]f}hjln] Z!Aa|}}m=~=o]AӆؒؓԌ׉={٠ڢ=ڤ]ڦ}ڨ=٬iٰ J=۶}۸dU̺=!ĝ/}5]}؝ڽ=]/}}}=ߒq=ͬ&'Vo̻ a]aMߕss(Nިas!1&^'$.02CG6~7n><~e-㌑@>6#D.?~JLNuR;^6XaX^`nM'Zf~hlsqTowtn:Gq}TN&be.j1p熾s.W)j'{vbLn?6rym~L N~3iz>A~QRP!NN Ⱦ~b..ƒpn]湎~ IQNa>1U>4鹾3!r(Q@1Q;AM4!,%Qon.$o'#_?-%>o>.Q /OQD-^ĘQF=~RH%MDRJ-QpQK5męSN=}zSPEETRM>UTU^ŚUV]~VXe͞E6b̃lվW\ڥW}X`… FXbƍ?Y2S$ILƜYfΝmZhҥMFZjTZlڵmt{[n޽}3;0oG*W]tխ_Gj $p^xɟG^zݿi|;p~0@$p(@dAk'B /̈3Î<0DG$DOD1EWD/\d1FgFo1GϠ3QBܱH#D2~TI'2J)kcJ+IJ(IK/3L1$L3D3M5dM7߄3N9礳N;3O=O?4PA%PCE4QEeQG4RIG jRK/4SM7mHN?Eƻ@J[:UTRW=4Vsc[2ӎ= j)WS"x .+V[wb2'^]MUSiWkl vSխJJ( pr&]Z$s so[YvZ_` akJV_9נkᴤbϾt!巠w Yc0_Ol["@\$2ȁƬWPe~x Fhm":mFi2^)_Z1Mj9@F;p!53զn;oo

:6 $IJr^U$ERZKfHJ[5UebV 12L4S#ϜJ4BL`npnj 8IrL:v~ @JЂMBІ jJͨF7юz HGJҒ(MJWҖ}}Lә8ͩNwӞ@ PJԢHMRԦ:PӢRXͪVծz` XJֲhMZHu/$([:utK\$k[ڙU݌`rfưuejW-֖xdWNfg%ٸ ez ALV2yi`)LLڈ}+bFƽ764c?Lե?f1y.~w.tϫwe/bޫ^0o[֗ٮ_J.nAHv E\>jC`- K[s=mC6l]x/vx谐iyc#/B>L*[ܻ2]l.{`L2M\Nf%9p<4,˩=?xsV@ ЈNE;ѐ'MJ[Ҙδ7N{Go&H/2j">uxTVհgMZָεwOVMbV>2F3$~Mj[~QCsMr[KN­v}Mzη]EN<,.T'N n'y3^&ZuAn19Mls&,=Q[=qF+ɗ.`qZ:i7{UԞt1hoVn$RKwk;g-"D~??$'xt~Ә|GqķP7HB)A}Vr- ﲹZv_cA7/oq=Wa+_…Ý^S?d}'O_)ſtX'nCI8=뷀&s?{xf xxPp!(#؁Ds(k.0284X6x8: Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0P4l8<7 DmH'L7PG-TWmXg\w`-dmhlp-t]m"߀.n'7G.Wngw砇.褗n騧ꬷ.מ޶/o'7G/Wogw/o觯/o HL@` J'A| A RЀt !1AW@'@8AZCG4DQHb( Ċ[r1I"D%X@0pm ṭm AqL"F:򑐌$'IJZ̤&7Nz(GIRL*WV򕰌,gIZ̥.w^ 0IbL2cG43,aŖ#m&6m7Npl'7vNj$v ̟-,vn3Fmn zj"1Рtw@  %g3mLHMڸ- OmPs5So:,KmR=@e`QԦ:PTJժZXͪVծz` XJֲhMZֶpkT ׺xͫ^׾ `KMbi:d'KZͬf7Y4i! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϞ$~ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫:AAk,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmxcO| ߀.8G! ,IH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-~ϟevgy Qv }g{׭ztv w/TyL/=^~9L#!xAOäݢr-h.Sok# %sh驷-P3_Km.-uoӶw_Jo<8dݻoJw{/SQl!IπC`: r"?i~k4B N;>?}&! ,T H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫nY$+iB D,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀N?n'7G.Wngw砇.C-nP6z:Nx/s<7ﻶ3/}[{| ۔$Hb B- `ۢ-]'u.~~}PNiPF(" Q-p Ԗ _lpLІ9!s(  : DiЈ5A[K"D"n0ta EoB,"6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WVl! ,TIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɒ#0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫.P  D,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀On'7G.Wngw砇.褗n騧ꬷ.n\xo'7G/Wogw/o觯/o H@5M! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϞ$~ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫:AAk,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀ӭMn'7G.Wngw砇.?\d-nE6 {`;C;^C n;P#< J/[o_ aŚ/#d#VJ^J JBKB ~-h|^' v@lAo{$E;[h@O6a1 P<7uO$3(y' rȽ9t޷pq ._RWnI^"G⭃y3L!C~^9R\"=Nl2B-8|L|Cʽ{w3$C @  bpt昇; : AJ` ;0/t2 1̉0[2@p`CC^y8p;a&9AD,"ꐘD"nq7v iEQ۠b%"vp ?ImC,?JIhT#`>mPml? y#>-H#"T?6 iW))L @8|e?F+Qo. BEMAz)ƒ ! ,T H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫nY$+iB D,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀N?n'7G.WngwSB8>>$ C2| O H" P쵃 .O!.>@B8.6`/<ُ|ޖ P>gK6Kt-Ю6hm?W>mB# o+>m? Ѓ-na>nyp U>jp\ IBL$UZ! , HI bÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ J`ѣH*]ʴӧPJJիXjʵkB`ÊKٳf۷pʝKBE˷߿k LÈ+FŐ#KL2m3k̹ϠCMӨS^ͺװc˞Mdͻ ulHУKNzhֳkνkӫ_Ͼ˟/ h+hp 6(S,F(>G f!Jn DD$( b,g^A#(4hܽ<@\ViH&ePF)d1יTf\vِ^)d"fl^#n)tֹ v'R駞)<)H衈jRN&ꨗ*>*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfve6 knDₕ.R!+k&+#L 7\O(V;lgZ1SXv,$l([p,0,4l8<@-DmH'L7PG-TWmXg\w`o9[abKW'TfsJA=KmxI~8щ{xeh_TBuA/n6TSc])\ YJ{U.ܮߌqz^;?WoQ_/+m䧯8Ÿ+'?P[#Y2:'H Z̠7z GH(L W0 gH8̡w@ "" HT&:PH*ZgKؔ,r` hL͐5*snc9,#G豏 Sc@L"F:򑐌$'IJ&k[4iNz򓀹(q<`w*$,]!G &[҂YZTܒ`~ƴ 'LM9JXjd&RinZ Y&PFgQ3"*NoS;)gwH鼧>33vR?IPMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚڔ^N6Pt@%WOJԢHMRԦ:PTJժZXzծ-^ XJֲhMZֶp\J׺xͫ^*5U8`KMb:d'KZͬf7zehGKږ A29ԺJz{f*ͭnw pKMr:Պ?$EIuz7DxKe y^*q|$ž&}U:W@S[N;-10[hc~bmcD$D, m pM.7$0K KX VKnHmF ~$ HBJ@':I8HĖ XQo; 3Fd|5d=1%ǸcsHH!|3+苄(H#b׹"101mد'p w/Yr]XRf)8Q K"/!-=|cI2fL @-?Φ 샘9ŵe D wX6lifvy y@ N`' fD)g춚>0Kܵx'|{62l' g.qE]C6{ 9|!;7 ` 0Ӊ>mm%z/ۆ;A!4.;X]w waw7;.+}!@N`w7-ΞY9Dfq+Z?K :øm G2p²=*;a@`z  ;&0l .ph5CO^@ޏȖK|8gu|1}Waf&Pmms[Vnz8w1va w0lRl%n`|FyVa0Ligz`|i8Xz7pvoу" fU~Qio~kh=n-q}D% a y{fh'fgi$ d i& /vG)RDc%F-I@fid8h1~d(djfQFzvx8hf' n0bpcXxAHx3Hq؋!>XȘ9V،8Xrsڸ؍K &yx7؎en*v%#0`dǎAZdÏрqA5aPO *^x/ё\T"y8?$y /2Y/I49YU i=)/C).P9<(F5IViYyŒ;d\[ey[LV4," *=czviGyyYw6Ԗ]җ%|Qg y3y2)!f5+)Bb *0iuљM !)2A-BI Iljlɩ֩cӹiy9_ٙMY\y%HHWT$X ڟ` z TjƟ! BYvڡ ":$Z&z(*ڢB.2:4Z՞6:!~ @JKEuAqRȄ:D'JъZͨF7юz HGJҒp&MPҖ0LgJӚ8ͩNwӞ@ PJԢ@[42v /d e#0Kq $>S_ȁH< #ɇ`#2cbqC^@&Dt|2Y%F^H'_ p[p//*/pAA@7"A2 AlȀ3K BzMV+2; ͛&CMjYӥrYgTָεwEYj{#vYlf;ЖկMm16n{_bq(v5o7G*~N`-(}5. ^qos ?8L.Τi7@DnSk.X.<r`n}~}D^g6U*|QU^ثKg+vV.Kf͎"B?tpxk"s'S>b{W>Qt1[$ãdᔧJ)b0d-f;AHNTeFdڷp @^g=mO[u/cʏ/[:>|syjOd~q'FSONP#0١'?WT ؀8Xx҄~؁LB"8$X&x(*[eh.1%! ,tTH*LHbÇ#JHŋ3jȱǏ CIɓ(Sd˗0cʜI͛8siϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp9 riщ|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼k챫JvI> Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0P4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tm>ݭ|߀.n'7G.Wngw砇.褗n騧ꬷ $ln/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(G0 gH8̡wpt06@eQP#1["X Ip?[$AOA-X=2>Č R6n$(h8$bxd?l:F[@ȇC,نQ= F!F&6) ,#$QQ@Jav܈$XYmCOn07;\a2oR!ŷم%\ `x`]< fT#>[*%d̓$Sw?ȨB@cL/%2P^΄@ ti_yO; HЉ؅PL` 5(LňD=)[BL4%T"QD @Y 2yiALLSH ^n*Iا(kU3cn[qTȶ13@ jA DO~ MEJ1DHK2&# u1i1zzND ջu(HԪV|- EFQ"Aj tb-.!/|ācN`XhV"(oe KQuEVd00DKL  : $xV_tjMEؑ&k$@li)=qP(KbtMI`1H_SՕ @m+ٲsU/{oa )7!M !6qH,6jlQj+ޥv\ټL˒6 poaq_|"W [@Yό?ܩ`E)WKI`0ʒ~If&6٤-_/>tz.֊U"? Owgɮd s 3'_8S hdً4ujJkn7/Q I'ӓL`%_ 'X&A0oj ,u^ĵi-d15RF 9BDџI"h5ǒ[ &H1C:\&C2 D<&*삐D.i o!%ķgTI LTOܨ 8Xx ؀8Xx؁ "8$X&x(*,؂.284X6x8:<؃>@B8DXFxL`HL؄NPR8TXVxXJ! ,x#H*H`Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*IbӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8B9-( [-6j!HTP?!.t`AhMpC?@Am&Hv $@@l"H\  IhC"$hIBE# djDZ$H2?8$gT Q v,6VB-"h x.l! Ƃtq Mla XH@-->"D,, b-͎|IyĄH@XDzE7EnPHŦ 0` {7a%؜$l7 H3x ^ 9P7 V60T6ڔІ`XA:V Wt * wnzv$J bP̠' yt_Zt󳟺XRT*:K0Ѓ{$l  !F !TWQmC)m TZaLB 2[ K؂P $4LmA!xb@W޷}I\S۸%`5kZ'h¼ 4oiC+ @-[L"6 BlmK9DYP1`}\;q}]e\C8U}[ JRyԤnga ѬT VҹXmOX|t=9^ZvͶ /.m`l, Qr4%V^9E"P`YOS~_3.A$* ]ȸUikLD ]LBNg_PP$1R{eFf8rlfy&Q/Y 6d#[U{=ldog([L ,(Y>l45n\bm@B8DXFxHJL؄NPR8TXVxXZ\؅^-(b8aX9 @9! ,EH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-d?FQh7 p-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯Fm_BAD$nhC/}%D $HX $+<1'xgCd @ N[ 4! 1"LDܑ I$lq@T0d`C68\̡ 9;L%@ab 8*m5((0 ³P_(BuT G;.P4",ɐ0flH(XP~wVgK#,7KQ$*yGvga0?#sbF? 4?jZ̦6nz 8Ir?⏃S;׆[DD婍⠑C)Д{KB*0 :F$؆Qr@݆?@H"@6yn/)HYYd9A&[.>@SLz,#.o @.Kv@IkѲ2; r" $mg i ҆O_K ; NЄh1v <܁tfy1OեuXM !' hl}Zv枌q/zk] `{t~ 7^.6YԼլ^<Px }@v~ ]!W7sʕ&HGxjϗ-0jvW0&/{QqpqW% TFu1d^UuDaDrQu|rQ^tfsj}}T&da@eBN7 CHVPg RegJlg5{r!aP[`Whb)&^"{_ z j ,`e/NYwWT/Fj' NE{NU3(Pa[Xa{J/p 6* Kez3;%u qhV8@`;_FWj 8Qjg 8].p`\V[D` @pa^N]Ì+TEIt@5xaG#jχ_B6Z @CEd֦v ;iwzOQ%_oxG5z!TPV?kE&<_iK[ 5`H38ӊXqZpVuPhHOcɓŃOD$@]L'TaHy<%\XCҵ5='X?ȕH=>tVRXՖnpr9tYv7('yÆvLCpI>ES{ڣES cuc K8*{=S iCNJNF>К*=Vp@{-USeM * U|3cN۰ YR(M ) p=Ć-ɗQlMEIC 0^K@  >v0$R:zگ2mP#9 [6j>ڰ#3٤9J;?I0 (p >Zm3d:! ,cH*\ȰÇ#Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8@-DmH'L7PG-TWmXg\w`-d$lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯>B 1P~ $nDJ%!m_[ܢ 9 GO kQA h X +_l 5$?@naX!"A<_-A$$mH ׾/ H2hL6pH:x#QlczH0-&(ˆg`@ mhC hq #HQR$(m 'RmF<+WXm+[\ъT&O#]?[ a-J?R $WH3gC't ddBMPJ"Fi6mpS -@A&jÈ`! Iނ`L$>yW󋔔(';vd$MJWҖ0LgJӚ{-tS$O 9ZD$ r2|-HUHRէ "mxcXJr9Z  c\0CWl#K W .ªy QMRc@*̼Ocm7u1' V$t6$ %fêH nkw @,tVV(XA{w +Vaۄ(7 mu{ 2r /p w7K܆UEm|svzwa H2x ^0'a/^>wXO;^浰/|>⵻u0a{\7]Iw{)3 l|mh]{X܁7.a OE0^1Ԧssw 9x1e, `(l@H8=5WRN&#: "uק|,//l^>g7 xq+E\ T3F9pHXl6yrSl÷ICs \g CF6UPښROS>7>66]+#,t9-IVB\׸8z+zy_,->}*W+ir IB6@"g~]G @ܞ[;n. b )8[P?@>Ձ%/GufפL <nejHkrGM.g@T ࿻.Kt,2ڒmM \~?Z+?] 4wNY.Zѣve+@llw'̲@z2p<৫a_ kФ >>cInVfks%'&.fVlx'wa 7vWVNvn%`LEդ @?'vP[Y%0]Z1'bZy2zc5zfxMxfPV]sH[CQФ.nq {`2|rh%fGcm adjp<[hĵcb tuhtj>[Ą򅆰S_*'VǶ^pz'W@P'V`X;OGu j'X(0T4#6_z;Ɇ] qc_BWM]@vKh;u Q;wTAfc(<|XOGS$@|EgnvؖZ`DGDI\f6w'IDƇ|fX;YoWG/?fi6<k(tcHe'Z˳ 5VkGETCGW<%W0Vt%i9C1VT5y!)p@ Uwf7uHJiAaL9T6RYXi8W)>e*jKH`NOIC>VT6)=C5X-i>HPk4a 0[f =%RPkau)}[< Z%DXym^HJʔxciXU×O̔ECXX$ m<^$'f)bIܴOMFJ%rɚmԙvBϴEz4s; QꨭL] % sjPzj93zs`* 9]j=j1'.pIwcNV **z9*J wЫI:j x R]. u 8b>[A:JM: ٱ +9!` TNɱMٖ95^Pj.6j1^z{NRGC#MP1K&  CyI 4$ ;:SvJ&:{ĩ ( dz3xzRA` 5Z M***1aN P RL*J-$0B1ۻB :Q'kk99 !ۿ/J`\ >j > >~H#>>,P'>[c$bT:6T|jc)˕&E=KCGLVOcc득$0V=+g9,;dFt@/F@Qu 40 (p 0>VpLFb\h~D(=yBh<>l CJ^9ⳅi|;F 7Zm6% ^ؑ s M?<\|l)u uc`3vpvtu9 j3>GӋ zj ɿ! ,aH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sLI`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhSlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/oqII@B H?IbKIp6H,@h! D҆/%h'$D| LD%A A O~.,P*h$! B!0m WA$O@ǷDH~m_&>$0 4T @hL6pH:x̣> 5<":C2򑐌$'IJZ̤&7i؆6n1OCe' (Zq 6mH",![/yָ?A86DjZtMh 8INqBI &+6IOsđ7ˁ(f)qҒK3OS mfC JO,6 Pl DD)ZNc J(p)A"҆+t*MBSg“ 8 T"!x&A m,%7;\a2ouFt -cAva H3 ;B8]TO|akX'W%Uy Z$RvZmV_ q/g @I  Y b3cKLa` ^0ۼJbiT</!iAY|V$Ȫ>-BbKK@$Z .j "Uvf$^6MQA[,!/eM=oK @,! ?Lo:v s6HLH~>, ,ke)Ů 29ڰ4 w-PYW<,-C.օlee0ɾ p& G YBՌi )ߐV~u g.q`-^[ emS:O$X7Jx@+fVauݯ3] 7-AX_xe݁Z7"u)=q\A ֭cM(|%bKG佥9ZÜK$T=o b+L)Ό\;R}o; ¡"gZ bn$c;` ߯-@k`s/}.\DwU` rE@tdz?x~g~sc†UedOF$}HSTToOj_[U;inV˄iA$@USdhY=KkkP_|e}i|fڐROXEcٶzDHtGwV<([YnLX5jӅݴom@CnP?_Gs=M݄R]&EP$I?U$߆-I% "qH #AM%p[ڐHA H @ID Hhp|n8AJO}$@%(}7$=h )J!~ ݗ$ O`_AIχ/(hL6pH:v1#?)@N GxF"C-$ A746܀4y$-ڠ$R!_$I%-KiKG2?gHT.+u /"F-_]C P)$y@$pې!AlQvS@';E9=mB6੍S h6wȄҋ?.8HIX"Lf$)HB}DCm -H$ҁ }LcJӚ8ͩNwӞ@ *m@HjCb UE-mԟJEDg[Ӏ4HЬva] HBXuċv2j2'&HŰ"֓=۠׽D4`z]H`/ oc.wg FҺ{ ނiq iOX8Aх$[mDayp*-?\^~_X Ajώxx?3@B4WɝqpU2 &bQoaЕC Q? H~uA:FP@4i-/…gp^h M=ۛD ZIZNdl*]Z`'x#yzZqBvzv8T`v ~JDpt '>FG>rR >I =iPe܃Ht|H t&>$xkeʅL y8ZyO?k%fٔE{˓[h@$YH[6\G9J9Bh'9DN;y<2ɔrٔT\P;Dֵ Imi%YNٌ@I,D,q q@NjArI3YT)X{9SoyN٘;Id N q9fɃHMd*9PYQ)cŚ4bÛIJٙϙA$֚ aFْqiܸM͹] &c4Mi|)3PMnYe*0$BW|dB` ÝuI`w yt V#^BN 2* 2aW;i` ߀e)fAR@:,jR bN N٣2vvX&X= NPD F1*g$` _dvNu:J9cHFiQ W٣(;v0p9 )Z:ԥ INAP&%RZea M WE ǒ wM)J/z*[UY2$g3 zʪri@p]VyR`ØMЬ#!1&Q%cj:ffK`d` M^U*4 +c֘vP+ K#+/ 3ڡ`::NQ%Ѱ*HkQ O901 QR{`SwP6Bķn]` f#[C>^;ì\Kj4>%*ɝv{KxB4P}>x%{_p>F1⓹j 擹୘{_r+X;U#~[Ps`[Kjrj>SQIL;vBhw!˻>.%@;Mp=zdkۮ; ˺iHÒ Rkh>}Sgę~(?կ؉=g@ް 뷇YkwP?>c4[D>[)>9>Q~%t>+A'>;y6>۰ *@#{s v^#a HPJV>9B<! ,fH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dJQh? p-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯uD/F$ @H/PHKB( [ Ig I!DJP 7$H$u! _$}-_AD}14H,H! ؾA G4q"$EGx?CB؆5GlC ',}ma G PO\:x̣> IH ("h9mD򐇴n!= TKH@ *m m6d)[ D$XRۨ$6̳  Ç/ 62 nh0S\.pC$P?ȁPH@XPڢ ۰E+LbgUyH|D&< hRg8JB7H#*mAGHpRڬJŋ2܆E.f$ v2/̼@)IA bo4mDyՁHp\ҹxͫ^׾\`z>YJbDLiը>Ld'?:2|-dRjU$Φ3 kNVX$6*R ^T*| |=b;U!^(A%NzI!VdH9U+$hMRW mF+(w /p@V/_0 mJ*o`x^0H vEk-UǓn{N AbjT HqlȀr-p@O}~5oIj3,^җݖpB`U ݀H2Y$k OVC YxY1R5a)Zb Fo]u@*ZS)ЏcR}J| 39Y1 mV[M_5 ,Z٫?9\A6Jo;T>`TpS# :A)l13.gZ`T.$Mv\S% ikfDu Z .xu4Md֧=t{!-IC_Kxo>qvpn$9~Rp D+"EO[x[.`|qFVqo~ghEMFK1K,&$ eh&ech,1{`aWeEa&gRuy U gQu `kiӆAedpaK gXoef2VJ(;EjsqX`?PIUP];bQwm˧MXDy\ aI۠D|n<^%g4 MdL3QMhUT{2ekR%ІדC 0^uxoI%_e@ ` j J>hq]ت^٪daZ ;*RJ:*D >Z/Js?*Rʰ5>(ꟅKch*[>ʛ#[>ոFy4۬2>J>N ?婙>VJ  Q'{>$.>4Xw@Ӵ>iPI[?cEM]iUYt>x^4v&H$p }jG|qZj=1`}1ÒsYްs S:?>>(әJu [>"aGz > ZSMS9y%NPq {"I3`w (n;=<! ,cH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l 8@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.;D ,XSDy <@m!II,xH"@m"@ {N;@B$4ߑ$AߓF$~ߎ=/:K7$=AGz#-mT-E6<!`GH0l1鵁w~k@3yw~@BI{yJ *w \xA }@@72N!HL&:PHX"mFŁ\QgxH ;xA8~O І @c1F7 "G=ϑF \qIp-yEJI6I4bgJ3^x-H$Qe-զJmhT% U!^00fŒ 'LI̒,/6sA6~7}Bu +xC|V|9jQ5*pl[)[.DUն 8v5˺2Z4y@@PS+ԙi> ֍6x?vd]> bO  Al'װpGDֲnho㢑3.> kufCdu=򂆔W Uj@[AzJZb@|c 2@C$1JV(0{K)0 k65$ۂ8/o*7H0C&۞r 7K^ .qB>Y_[M c ⰖDB3je 2S@pʰ ̠݁X-qvsUk벖ڜ]@ 1.l[!{a |H36؝#f@Ư-k,_ٽA0@,5lcA()!6iɶW,BvFuen*:){#y< bӚE@hЇC%Jc%86"=Gvuᦶ~Q=/ &bwP8rNOX} V7H95! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ ʑѣH*]ʴӧPJJիXjʵkׅ^ÊKٳh1:J; ۷pʝK.LO˷߿l%È+^ s-ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MFoͻ Oȓ+_μУKNسkνËOӫ_Ͼ˟O}Y(d\& 6 WD<(Bfv _$h(8^*0(R,h8#Tu@)DzcH&Lwd_56)TVi%Q\ٹ`NiUl8"bY)'i(F&z矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&̮gF+ =H@m+kN$GbgK\c4o櫯h>ߛUl'IG,Wlgw ,$l(,0,4l8<@-DmH'L7-Un;mՓukkX@WM$+/c\6 n" .i%M5ޔU=Bmx?Maur6G1 . dIcgm-ԭ >'虗nV#xT~밫|^{9ꎜ/ HL:'H Z̠7z GH(L W8aą, W8̡w@[Hĸ԰HLDܯPS)It IHh.zQ\D21"X|Hx@Y}mDGAYT?< (BNL"F:E#ZyJN%3,qP(J?Qw}HT;Q#VٔHr%-xegE%-&{*Zs8<2߇n'< Iͦ2af5Yd:QzDr(#7hH[Fzg~fğR>JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0L3ʙڔZK厰H0fzCW3e9ѲEle@{6KC+ΎuIϤ˖δ7DAsӠvmQmhԕW}Vա~'m][h-%\I-צ5QFmhÿPHld-B6L {%?l1f,٧Oĝ6 ra7$NFMsNBϽ nS-4ebm$n7{mȷP -/R{nٍ8I$,@nqW-- `IJrޑXvU[.,9˞n_~nm,;872{ Ǘ>{\7?7v+`R6%PFKa2 ^pv)}kz͝qrӝ#F?{ob A.lyއa)H f0{'\B n3>r?#h"(x ^/?@/Npw1 ƢM6^o9?l\PeWyG |wnp ԇ'7{/ [}wxt IwIk}g td{ap!Pw7z a}yg ȷx%lGt(FȅsN>ExsAg~ Hz4j~x!x 7{'` a ۥ [sesxuj.w&(Us$U:..ׄ]ʖp['/m.yrp{j1pIrWVɧ5nq/vlo/(. W{kb lhkXlA`3'x]ܦl█,`8jHy@8M r8]gyfm'&XsxsngHk\)h ׋yqv6'ƆuawͶf6-5NJXsZdntfnwqvwl%BHh0nug^d`|Ng]Ia 8{˖kPl vҘdNŇpT5wp`^`z|3瘎~|~sxP u Xsng Uft: N@xmGK_tdf@koeogn`y:9Eg7z)e |m`n >8^"ys&zV| xn9u%` qllqtuĜ`9XqF hn_rIet` v`x xscrF6Ixvm ۦ ("|ЗgjqtrG]ʒIVujqj{2q%ysWe'oA:ru]iZ@`;O97I-5@Z*v aIp '[ ooiRpߕlaq \w tsڨ:-!I6کIQeJqTWu@ᒰZJyzqTH:?!eQAe|Ϫdڬ>̧Oo':c.H=jغdZNgZjwa@6fI_3pJ@PfHp*ᤃ) pSf;Q:-pM@*0* 0:+d8'pQ";(Ӱpڴ$+*+3[=DzAKcYIFh3h[qF9q9ƯAw:+ ig&!fchyƪ^dY+`,a^f$)qe1k *;,Vpf(Ɩ1skRC cۂ#A}iChY˳asGbszѸt7J 4FqM+s~{khj׫5KѺֶf+izKQgJI6Gq KVs[- 2kx AvGth{ cJqа( y,y+kòRORkkcz+b$)!?7,[SG 91<\Dc@Q۽Q6YEqԛphDaQ/.[R,N0OR3! Dz%!S|,L&;A,x8+lɔF-LVZ~|^w<t| X 3f;& >˒챪̩#!K> ͒8a3CM \ҬSaBqDQA&ʨTKJ$KϏ$,. kě\L2iH}!m)|% ΃ Ѥ$ρA!ahQQ˔rJ m#mEMGW='MQ6KǀLj3: &_\Qu>,?&lÑK?a,2%P-p|!:QxSb; }MAm]}Sa ֋}]S=A:&[84ءiDqVa1vZڱۂesJR GIvfu6,|mQ2rھ]q6|ѷ>ݴAE-zx CsJPK ۋ cMmgLTbF$j+ @V_N->VMt<ѵ6{<FlI1+QSgQvvZ.X~)(l]P]hvG~{۾iH|(^~7芾>^鱂疞难>^{^>'! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ !ѣH*]ʴӧPJJիXjʵ+W^ÊKٳh3J˶۷pʝ ,T˷߿=LÈǐ#KL˘3k̹ϠCMӨS^ͺװc˞Mڸsͻт8<ȓ+_μУKNسkνËOӫ_Ͼ˟O>Ņ0oϿ(h&Y 6F(Vhfv (b~#h("Oy0(Ph8v@)dz=iH&dom,PF)t5Ni:݂BV`T@dI~flp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸bkhlRZ&6F+mqLktZ^mHÂmR^k.f%@+YԸ￑ l' 7G,Wlgw ,$l(,0,4lsƷ|@-s F[UH5 =QK\HE-6aQ@3GkuEm X6!rwRBTho@H4\m[݅G.yWOvxV?w07J=稷;:on>^A-I.O$;k|G/|}so'>ϟo觯/o HL:'H Z z8GHB%8 W>!| g 8̡w@ XH, ,&ćN#b)ZP.z` H2hLYTQ.o\HGȡ`BUGcSKAWP@HvAi"-#IIlGgbhICv29(՗BF\i(WU8A%+gٰP>reAKZ;0aI$L2-5Ќ4IjZ̦6nz 8IrL:v~ h)ЂN6AІ:D'JъZͨF7юz HGJҒ &MJWҖ0LgJӚ8ͩNwӞ@ P-TʡiF5BԦ:PT@[RXͪVծz` XJֲ0*Z"@$(Ao% D0YI2I-6L\Z d 7 c= lbjI h$pREkij) $[=HJ` 1n@ 7i-2+6KEnZJ[^vݺ#7 o- lA~kM VGUbIoBϐF6awM\kWB|+gbmԖ̔nYN$4@"rJx+sH2?93ktI:imף9B܀v`=#r;ǧsLK:!C8^ _.s\䄟 !y?cqto\aR6o9;e=_xXh6waRqi8i? Sw' @ן%~_WZrKnywA1) p jxߙL҂w7gwyzYvt!pp1{0igiw?vds$moM|wq@}7֗}3p`|wm|)$~! [ׄ~3 v$ W{VfpGg!Wwqi&zwhf (FGKL(swp@Ag ؁Anw!v 0dIІ'}jgLog ` S Hvw g6VWre FGk;y8X o f)Tcfv$X֥oRp{ և}3Љ'xo Zmh u`x.dlȅ!xL&@w X~VP5pnԸqQy5.b8ZlHM('XX](xM b8eĶmtcHwƆ$v捞Efxp629wpm Rp3ȑ B!VdjpfxFf$ɅV􆒶 sPy N7(}yȑgm]菽j|t6eh&M7ԊdxM(۰{riql qiZxpf$\qĕ s )qp iR0q']av[G~'~es Qu4z @. Z⚯\tQ )MFs wzg К)i x{ 8s'{9){zY_`f{4 ؚ%`v 'kQGsg7xS g_Zb5{zp u| n r r . W Xge0Zf z$fEA( XGbq# b7{\ =؞v`g_fjup|o tO7æpnGdz*_gAȋA_۰{qy7yqbǔEnxZ{b碚wWmc ה*r ?):s AȆl_6򳫚:ZzRBny F:Z暮ڮ:ZzگQ;T Ӱ; +{+[-u:%(#,[3!2+Wqeu13;6>+ZzB M,75%6 N k*U[WY1k4SK .jiK  I' @d.@AaK.*n`bg_[!رKڱ++* Aip v+kRk/Q.0+m;kĭu ;k+[Ur`Qbv*zjrK1cB![>:K1Uk {M +Ra?w W侱Ti`0 # jQ`YhR )"R-/\[m++C$(5; //{+=1E|HICPeR|XV\Dq^O[:f|ƛq  Bx3er"LD"DmpC$ 2E'D%@B H- "S$ mhOF q' CDpT $SZ ?,"$$H~((ɑH- mhd'GIRL*W ĕ,gIZ̥.w^ 0)*!<2odL4 ˌ4IjZ̦6nz lArF:yӖ?$@irI\6a V$-INsSBP$Hb4< Juzq ͨBaT >ЁPBF qCFa6JR| fThڰSцL?\h Zj3HBQ1)҂ uC3bՁatjW:"(bZ b pd)^"Jb MZE8 V @M|!BƧHA=LA3o}j W~3-hg HwE?|j,5,[os5dhyw dK<6XxOZFbK^}Z6}y? Vm{u/:5-9 `6@7 #>.pt`$ MqP&نPXPh )<@Ì]!B߹s$O3!$})+Ib`!V BVpj fgQH:!SNY3G-h9ea2 w4~Y VCWO:: [v[C5-*r2[rPh:~GR c&,l^zVeS޷8`~W@/nv|!\P1P[4Z!la`3fV mr·mN')ۢCMz/ hfMeb'dv5+˹]A,4?u3\R2 ֘Wf"IcV;.쿜JT4ikRF"IA.B ~^j *gzF$owO| [uӒ9֣4˳rLw35?B7M/.1$E} @fe_åPE R {Uw$ eT6PN.G\S!e^D Sdo!eU(z%R|T6^e[8lb[$ ַq VS5G\`gZX˦ OZuPPhDU7N۠}EQtXۀYTfD] ET@|Rwh5xxR0PNXUg XmwO%_[geWZdhV Q@7JjhYPa(1E4PIo.\/u Q3u|c'RUnD/C wq(NxȘʘ)86Fx##vظ؍8Xx蘎긎؎8XxqL5y "T y9L5JdA9&y(c*ْ.#S094Y6y['"8y;¸>@B9DYFyHJLٔaH! ,-H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPaJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm0Thp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯~lP I? I$o-@B g?7Db%HG&@$XB$!!@'B||sCڧP|kAlɇρ= 0t_$lD/ hp% a .z` H2hL6pH4ix̣> IBL"gFن6A>GJB$MxGjmQ Yb؆ V 6F9KFϑJt05E=l (^&?>SD4+I(CIpe@I Ԇaj%&C IW&̓?JBDmpe19"GB9L[3y;%ᏁTT E"o:<=iF•44H0 w7Vx܇5P*ocf0Rp(OMUz}s@; Sm*;c?d#0%L R V:SH*GʼϤ>-%"f]0Y .'la]A 3`Q+23$\Mn XZ x$mr kv]f_yV5\sՕD+rTYh8R򓃎W:[Y~c 1lSg_h$h%$M\VH~ 'E)w}[' D /2Wpb46 `'M)kBPѕK^ДېE+K6!@Y+8%]c^H,0Nr-PmS3n#7bpsS~ &`#7$q;UaB {Syvjսu06v۠#&.RtvTĒ܆5GؗcpaEbTzPI~KQ$PxN'K%L0b^IEWYN?0GQV5LP KD= ڰ@D?m`x2U=Ҥ5J  7=r\p@qۓv~HXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx؈81X8! ,eH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmvd 6r!p-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯4A/@ /P$_ncH8$}$9}EX` $|(2( ,Hp m( H `",C2 @ HP K04aWYPDKKL ӧBTP[aݗ,|HG̱x̣> IBLq D qdA!F @Ğ$IdR pCJIHb~8? KR$$[$І .pEF)6L-qHfYQG@Ɂ @'a6H %%;۰ [K4IOf@ 6St%(e$ td* bg ~EPT(>IFҡmP3R}Þ̞%)[H%54qLE/$RԦ:PTJժZXjFTH-eMI@2- I]"!!%=[ mJʹroy *ʏ#*ANf:6ɷ nkBzl芐Upe51R>J ^r 9AAYFo@aqvl-oSdp=l`?.ASs9D  $'!ݗj/;|c@f0Kb!j`[@9aqAfK@ؒe^(EL'! oigI42 ȤAYQԒ-T+7o Ne|㆓5>:O릗%47!"sY!Iʒƕ`.d{9t1C'tiQb8HpÞ? ɚ46$\ʒ|b o` B`$t I A37MQU8f&۰R+V(4Az<&|/ЮL|l4u'1rb8+f,wZѻ٦m Q NDcn ILN+ AcB[y 9rvAL3@"qx թ[_,=m1)x*/#dkh\隦9 P'A\% I>YM\0vK@Z —̭3CB I`\iR5k qgo{n Ħ 3mڜ\S&H R!5h䩩+~ L d@0^gо !/k~{qc҂%0Z!WYf n ~߫K*͗ŧ@zh{F -ż<i%eoodU\P](|se jr0pmP K\'~%i$ hfjxI襂PVZ wuIzMY HFxI$P5Zփa'xvPuL%lucOLڐWqyC\#MHJX5%?W}QH"qCP%6W>ФYؔz~Zu؈8Xx؉8Xx؊8XxHGUE'=ID> `s>4NPI95CeÏy>gqT9ً2q4 >(>ǝM%: j">l yZ6,Pk ?(D)/>XCRH[ X $p 8YiÎPM*> PZ=٘ɗ78 ?.pk2| 7 \CqxjZ>i+>9U)hoj昩~Z@Dn>w B(Z<! ,dH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۳ͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-,@f6lIQp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯BkBoAІ@җ[/ m-@/) P} AHH @ РKH#Aa6%PUH>F+l@ja @HxA$ A `([! `D۷D b pcFt#:x̣> IBR{?Hc@$IG>R|6(7d A( E$Дpb$#ن/܆?lQ. ن6$iCp$6hj njJh#68E[y=7 /BAhXf4F!tk׃LNDPK%0?Esmh 2H?d>tQQ[E>$TPXi؈8XxH|؉8Xx؊x5x?Xmh8B r S ӁȨnQ?q MP.Ҹ.MJ 阎** > .ȍ*w ` P,)@p X Ѝ(.(*.4(M (P),iYH: N;98O6SѨH*w*hf v9 .験ɓOhnÕ])uy9pv .`Ky-0@-`b{:ߠ * MPГAَj9* =|M)Q)T3 h YcYyi@ɛ)Lx qRrp6)h)@a/PɓX. a ߠ܈:r$M"f>)I\ؓJi)A:v iPI(.pA( my u0 sAJs N09iv *PMTș= mЏA\SNܩٍ9~9`O.`)q8DNjv:s@y?)DӘٜf ;r:H*~锩٤N9h /Zٓz`h) iA)M}ɝ) S9ZV*x*Y\i3pʛazǚf*rڬkZ88x:0rЖs  *$9RȒIѦ3*]i*:9.`hg&Z:X~!)mIPJ$K;)ڬ A*Ɏ&jJ4c1 QvagCړ.a:;h,'#$`5+"ѯ ߹:$ОJL-ڬlZ;; RiXk=?PyIq jo;~+}+pP~J*劶 ?MʢH>ؠCǺӲ{f[>[Z*>#FtlF֚>~}$))3 KJk?۰ԫ=)X>׻z J[+?DkG>GĵNl*T `CPQ ? 9PٿҘBkCԊ=0Iژ'Kj1Ŏd*|>vjK>r4ı9 G7L^P) ?` њLx?Zkr rfmHc;vrO)މ>I@#ӂnsTM0:ƒ=F=! ,aH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sH`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w5 dGQhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/>ngo@?~%$D$IQB%!DT 훟?!ApA% 6Db~ ' $mBP H?nB hC?D0ᗄ D J*ZX̢.z` H2hL6񍎒l>΅~ IBL"F: nmՋ%q [ &aBF Db$Ajqv\H+AJ2 cxB a?($A.˷Ac;Jkگ !Cc!`+X=nGH"3e6 h3ٻTT"@:^NZJtZv@iKܚ mr+,Qr?-x%p6h՞Ѯk?_b Sx3G(#%IZr0w\(AILAsmsn}7Q%>ן].m]4sE8"A+vsկ@(}S^:ͼW?̘s|bҝkzɟu%8 OܥnlV"ʾY7(y,xvpNxϻM~OO;gU+NӖ_ܟGOқlW ة~$ow/ժA|^/XO[o}u?B-"6O X8w x*A! ,eH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPaJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm0Thp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯@#TPB]~mHIH$֗[HW>I0G ?M|,BWI j` EP~H w $ %E :E <-886 WH$' ~mbχocH2hL6pH:xc },?$"1`Wml-bڰyHR +-, *Y$$ ymPQHwH(t 8ÅYR86&"'6WE ؆9 yI`<6DHHwF%'.y$ ZE 1p'; m@Z8L6QzㄧA"$`RxIOҖ0LgJӚ8 $YU &ؤAz[Tjmlճ@*N S-'NOa*AJ! lj%z%%-4z0e *WşT ;PP >7KFL@L2REJgBܕ"mT-M)g[hc@!A&U]@ai= Q?a߰b\$'pyELUm$ CDW26U'zX )|<|vH6JIĶy-c6Spc HusֈEeq3Af@&| h,Kݰ2ijora@GpDӞ~sm"񈝭ahĹ,ib$og>2Wi2߸l! /)h8b H.Ox~Dn Z\SP R9AdaFޢaq"ה߰&W⋐jMd]wg`tu3yUиxW$Jb+a ͯCZvFY"w"x3şn!?@ Ҷ-;>U rKxV)gnw/&Z\Fb1חk)~֝goTrf bXdBq i}98'" "z3g0wYN qq9W@(d@vXtvhmlszpeDH"tDp l[(( UZD$GzQC0=t9Ux؋8XxȘʸ،8XEA9 Yэ3Yx:US ^.MPMD7k. 1(@ `ď޳ zƓdq@ .PD 9\jy) ( I<ߠ NXNNVX-?(`b]*e(\U`!#` KIr|ӓѐ.@mq ZQ ;vP-NPI;s' }Tx؈:jl)M P' ;;).Õ& %)>INw<1:UFDCI:eY9:_6RIR9_iCy>ͩXw>$Cg/ ڹٝ~9Yy虞깞Ysɞ9YyٟL) \j>A$>cĈ:8 >7p vp`#N@*㕂>99モ! ,=H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻߜNȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-d}%f\p-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯 AH$@B W H6HpCTp}l"QbP%@B 7!H6\0' 0 w? a88PF( H D  M,`wP}SLBf2hL6pH:x̣w 4@R?lQAhmEx|" $T?$+{}6H@c"8mlK` r[6YlÊ/T d "fR$Hai-G6D"2?u~O4g)m􏃑p~ @JЂ-PqXIm|'юzg9Ex#(ܙ@d`F("s'6BI?(#kQ7z@Zy_ӁMB!mq6JQR ]?.}FD*U=KZ1}\F_rTKdY+(ɎuQH<` 2YQu+e*@hf0)\f"H+kMohcrX;)HA19 blmE;,D3g$pԮ*m3]6x8 -8U]K$+@mH{ \E9s ӉdgYad/G)DI@f +X8^5WC$E59C.6q*F ^)@ /s t؂k[ q5r\/YI3#Q?W!m7!?l΀haB0/[ +7g X QA rЅ, Ex~}sjReZ[T1>f,ʗR:rmqLN36*aK6#1G gޢ6*I? H7NI-c3w)]V֝?LZ½\(}ęs]sU7*?#5;_Xd6  'd݂h`Q ig3S@긴6@_02}3,^=K(EɿJس$&I@2 7^lz i#_czƻ;mfWn$gx6!^a133A^Rrٛ/5 _Dnc2`pddLkz#,A"*o2$A^2tel]M肫lѓcFUȏ밤Gp sK"HH$(a,.n?,ᄧG^*kY~}eX2dMG~ N` ~K=g^ߴ xS s` J\JFdX4׊> XRry= qP@I- )qh=NMesSAa˳ r qr ֣ *h(*=M@HsArgpt  e3 rP$Isk8F?|>_y! ,~bH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS7װc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4gGB8+9\D>-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.$@Bm@H@<{" %λnD"| I47 'A$ 7" Yn$m}BF T%FzAHH/oHHoL"=E`6 7(L W0 gH8፶1цXmC >?8%0eDկH@ ! Gx6[F"`Dqp^"(h ( %$րd8pBL@ @@xp%$f]<AE2v Avʹ$ @6h8>gtM[98AXvuV,+ Aqg}ȤeL(ҺYd%\j9B!mlAz6ж^Hݷʡ -[[.@6}Xތ-W~{ r 8kHN܄NjY-nz݅x'Nq@ompDX GBѹk7&f\njltjyc-Pf_&Wȃ&+(v̅<[[&Ujth* Opm5:ηhOް[{;޳! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ ʑѣH*]ʴӧPJJիXjʵkׅ^ÊKٳh1:J; ۷pʝK.LO˷߿l%È+^ s-ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MFoͻ Oȓ+_μУKNسkνËOӫ_Ͼ˟O}{qϿ(L h& zEDFh6Vhfvak~($h,0"b4h86_3@)dg iH&$}EPF)THXf,ZveAO$ɗdIOc,ؚp_tibvz矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&lzx.FYv+;榋-kRq9k⦆~mb/,l2 7G,Nlgw ,$l(,0,4l8<@-DmH'T{Sż-TO-NWsZ_5Gie}]`.Ipvt<7WwK&H_7A{6TDwJkxWn]{W'x[.[MnXR=:鬷~{rﵚD '7o HL:'H Z̠7z GH(L!܌g0L cH8̡wn? 2!HLqR%:P,H!YHD$HFX!x)b5Vq"jt(%կq_>x AL"Ⱥ8ԢLI[ &q,CDZ*%*!M#Y҂Uv TTޒL2%0a}PӖ*rc:)<'gZSR͜L1wAoLH3cr^ɐ!D<yڳQdL>9) @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0))Ӛ*=٦MwӞ@ PJԢHMRԦ:PTJժVͪVծz` XJֲhMZֶp\݅ӹFvU׾ `HMb:d'Kʾoaﴬfԣau)A]A ട%-"".$T-g@mD'e˙ @`%L*fo`` KH9-ԅnt&C>l`+!hKA`iB%ҷ/H w2)%RԶLR]ַVLNӂidk`3si_S.~0hu񴱎-@?2d G&Hd4YO2ld*#Aβ.{`1늎f f!oH5C!Olg:3%~K#[=$MtPЬIMMlZHk){ykZ&Id3[L6G2hVIn{;}-"b,=ucոH1nw0O[ ɿ? jWʜ.$ pc;"ƷrHaD^qi/H/3y3H =Wy6GvxY_0  Jǃ> @APn]$Ńgx6ո@oD5.j`Oѻ#KE20:% .$.H풽o$q3A|m>?]3Bx8.d1t*y >!O? ?$9R.񬜢m}Dwkأ&ܼcڄH֏O=RX!|Kt?)_t 7 I_?)[sOb$6C~)[7a~Cw 8 - >8Xh)x&H؁ "8z$*؂.0284X6x8:<؃> x()aF(~W p m#-Să,qaRN@Mm].Pf* MQQ| _FMnEX1>!6gHbx78mg'hq* [؆,Q[r(kƆS m6bxyWFx($`c a hoXv]iKhM |88s=8hexM؊XXHGY3 u4nwoXJ+-ih(xP a.hjs8!WA\Ӎ3.pp5H45|@ySXaA"Ig(i@)QȓV1=9ѨYȏؕ6t=rkHIg "'fIK(W A C&q.٥ZȆ,j3HnfsZJ:F3i*9}Sr8js'hR`H^tt17 ^(!Uq<sdʃǺɪv0tW@!yD%{;ڊ B%@&qY  ] kp!+#%*X)y"^@hs*g9KBx(4LFӺ+|bd{oNI!| g8̡w@ bH !jɆ 1X0PH*VV̢j` H2hL6U\27xt&$X|n3@.%q  Xh&02EȖ|tƋ"CPK!CncHĨDXit<IEY0i_U2afr Ќ4IjZ̦6nz 8IrL:v~ )Ђb:yAІ:D'JъZͨF7юz HGJҒ &MJWҖ0LgJӚ8ͩNwӞ@ PsʡiF=BԦ:PTz\RXͪVծz` XJֲ" fM+Ъֶ6nuR㪘QitͫgU…mg*Xd ,b,d }ed-rX^6TgGђ1-jWֺlgK r;[ pKo:>j.tKZWHMKvǣ<9wRdnOR[E| $~`{" ڢ+"*x8I05_K ưK Lb qK".vac[3^ɋ!cH"1^͏ 2+9k$9N 92(TvE0y /s<,$@2lQ3R7-Ƴ@l3"21;L ŝ9"Nv0)x,1fw2qhLx泛? wS 2Z rxuBF{%"2+0z׿Vu}fq^/!=f*bT3.`K Iᡝ` x2o8MW7%< ,Nt @n8Oplȁ)n W<"w)ƒ 3KZ$Y唟y,ބ?n æF󂤚x'j]8vU{+?ӫu<_a #?~C۰)LA,n8 fk}>lbT?$x?|ccɺu\,?1jʓZڢ}y`9{asOOϿz's8Xx3F6] VU~6(*7h(!2-ax$؂.XeE?4-:؃8U~&HAXC胿W3a7Xq9hׄP8TJXXXHwWz%s8LAu|eaEO@28(h8 `[q腻uhzchцх|( XXWZc8VR|A2t 8!X(/!8JP-! Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0P4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tm>ݭ|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH 8?N0J 8̡w@ ҇H"HL&:PD/H*Z[.z` H2hL6pn!:x۠>h~ IBL"F:򑐌$'IJZ̤&7Nz (GIRl*WVw x,gIZ̥.w^ 0Ib.KL2f:Ќ4IjZShMPZ@! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟIJѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫")PKP,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.8 n'8,G.Wngw砇 H3L3B+ {ANH.沮 Nlb %:ԓK|3g:B/?/ >-?.ȿ|9#@:u37-[s6{O\ UtЅ gHC]ܠ 'm],AU8Q 8Dsq%-u ,MxDr\"l0rĂ[Kڐ8*nkB-h#;P\ ( &[dhH[9<@qIqmC|d{HJZJ@! ,VIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫ni ,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗N-8AzۭB_TAW;A/oƞnʛ?͏˼ы;;;O=?b$2$lr?O?js-3H [l~A -}{oق`=y[H!V B0 cH8̡wb`^"HL͐!D,HH|q]x.m "P0 6K}kd8Q"ͨ>4bv@я$Ȼo Hg [/`<J [\[/J!d m\ҕ_Oi%UJ_vI@вL2f:Ќ4IjZ̦67p+ ! ,T H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫2$+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|J-n'7G.Wngw砇.褗n騧ꬷ.nx/o'7G/Wogw}i|o_ n m?痻> ?@@@$p @jk@6ZІ $ڐJpml[-l]Χ [`6LBG [46wlPe :H*ZX̢.z` H2"l ! ,TIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɒ#0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫Rk,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx| ?n%7G.Wngw砇.褗n騧ꬷ.n{҂.o'7G/Wog궈<.6/6@B{޲O"(@np$%> ܖQHB-U6ؗAm#Hl!oB Kl:GzǭÈ, 6:('o#Fh-0rT,@" NO"(#rqrLF:zrGBL"F:򑐌$'IJZRxMpS@! ,V H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϞ$~ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+E,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀[Mn#@7G.Wngw砇.褗n騧ꬷ.n/o'7G/CO^_ݗn38wt㗯6ï>S?+_ 2% ?[p|+6 ( mHV![HwAmPH,=Yۨ6nAy{!6l?H\k(88v0-v*>J - n1"%0r*11^ql .q aL Ahp[}#F:򑐌$'IJZ̤&7Nz (G 8h+ ! ,+IH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6 -!´fv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n[ꪲ:N#;NnO-$|J""@S)Wo}n$ԃE`7p@e)PphA̡wMB?l% }RDu֏}N4 z(%2rx>H (P?.AP*" Ѝo A+p dqx,{?`H ȣ7)3($%H,2Y JKB?I7#6Q w6Xq#Kd2QLJӔeLC:n&ȷ j%p6x;^%H-؂ʬV9Rf &I[hà83q9-L$ĆhrъE9-bTz(MJWҖ0LgJӚ8ͩNwӞ@ HZ! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ ʑѣH*]ʴӧPJJիXjʵkׅ^ÊKٳh1:J; ۷pʝK.LO˷߿l%È+^ s-ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MFoͻ Oȓ+_μУKNسkνËOӫ_Ͼ˟O}%Ͽ(h~FD 6F(Vhfv ^"h(N$0(Ph8@)$z=iH&do,PF)tENi7mB`_d)aflp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸f묻lR!kn&6F+-rLk-u\HU%In+nSy+iT￙~ l' 7G,Wlgw ,$l(,0,4lsȆ{@C:rF[sG׹I`.I\Du`suXcWHKUAg͘6nfTD6KWw݀nݰr{C' XXSI.@gUv~*ݛ~-ˡnsj9xSnh+`^7ojI=s_WjNچB6e/mI yRMIxK6>KʑM'CI- Y (KJDR_ze+giQD FtY^%0E_n&di91Ќ4IjZ̦6nz 8IrL:v~}@3eLtu3-⥅$ (R)ъV qE7юz3H!Бa=JWҖ0LҙTcNwӞ@ PJԢHMRԦ:PTzRU4*r0ծz` XJV#BhMZֶp\J׺q^tֽUD-_b& 64a!C.s9ˤc)Lv2-eE$Q ^eКV@8j_d͒K}]lgK֨nw pKM. \|ЍtKZkh=KQnR$?x\d5Ō{kLX};'[&L [85H/xGLb|!".SRbְ!  򂂜X8V 2891C "''gyS#rfI<ϧyEn BЈNF%tU -iҘδ7Q /(QԨNB:Rխs5gMZָvs^(MbNlxNОMj[ζUh5j=k8D%T<&ԑK2[Ml\۸\GKH'Y(roJ1 .#|Gc;!7F:lh3!q6E]דc~¤|>4g ' ,'㚛\o#%4!Aw\}4~Wa{9R8HJkX8UǀVxׁfXŦ(0].4xfV8:k >h^{A aB vVJbA(UԃXc^`b8dXfxhjl؆nrp8tXvxxz|؇)"wZHy&! , H HbÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ pѣH*]ʴӧPJJիXjʵA^ÊKٳh#J˶۷pʝ,Ԇ˷߿5LÈǐ#KL˘3k̹ϠCMӨS^ͺװc˞ (۸sͻbA ȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h bE 6F(Vhfv$h(W,0P4h8 hW<@~cDiHFL6P"7cTVi-VfZv%I|) Hhlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무:ꊶ뮬Hʫak&6쳢aIHqy؆+isޒnRk9o,l' 7G,Wlgw ,$l(,0,̒F\P3^B-n{Eo|7_|]q_.܇/o觯/o HL:'HA#77z T(L ÓU gH+w!d 4ĔPB(*i.z` H2hLPik91KH/70g{lJ()ҍI I.(M8d(4-+YHNg %J%*vM鑫e@ydk,w]K%0I$aƘAIf:Ќ4IjZ̦6nz 8IrL:v~6@PrMBІ:D'JъZͨF7юz,(HGj0(MJWҖ0LgJӚ8ͩNwӞ@5)7=RԦ:P*h)ժZXͪVծz` XJֲhM+ֶuX|d Ux%j `KMb:d'KZͬfͲ hGKҚMjڴc^+PkӲ5O ⢦ƅJgW:Ѝn+Zͮv[mĻ xKm |Kkͽ~2LN;h.'l SX#7{X46x?L=Wj.qR,8αw@L"HN'%;9|L*[X)%H2m\_m &^ޚv'6 ] iqGlʊBlc$A0.ylhB%2iO [-D+4x3G*z!-k $ۨ-֌BQɻFK]{ZŖ?JpתЇl[سic؆-au 6 m_k 8,QL"aY'flYD]_Kܬ K"Kʑ#~ \Y_ܿvgN}K2H7brs|<?M ް"P c.]+WлZ0t]$_ױCzKoG 7λY.ްݝw ܅Oe|x;^ŕ7s!.GOWOboI}gXS}7q{~wp5*;ЏO[ϾdsOO?Uz#R@! ,tTH*LHbÇ#JHŋ3jȱǏ CIɓ(Sd˗0cʜI͛8siϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp9 riщ|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼k챫JvI> Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0P4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tm>ݭ|߀.n'7G.Wngw砇.褗n騧ꬷ $ln/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(G0 gH8̡w@ H"H D± O? IDŠ6EE[#-6bmTE7bۆ-HG: ~ڸE!y m}"( 3/sd% KO\d%P1H*ISnr@I62xIHS~zt?lP.JTXX0Q{6lqNjc!_!&L:v~ ()ЂMBȆ.D'Jёv$p/o(FJҒ" \7 0M'F$IpCt4sJ*m`D^# %mQ 7}.@ næ#ET珬#K) J>TI[R>ITYȫ^*HۆOp]BrpbuVu@ ZoA3,f7;]!`+ARmA Aj_ -; "`]UEqj݅%Rk $HpBڑ6zQ|l6ۇ`v5 zVt Z9DĮI0H!IÉM@kvEXk{m4 PHo2'ȠuL[ ^al#Yܞ606HK"@*SY7HG]@$@/0vDŽlۆKHqo)؆C!3#Kf"]`Ȅe\vQ]<`q+`W]lCg $V'|Z;$N+1]ߘYF@ sjC M]x{.EJ7)\aWdЇNHOҗ;PU! ,  H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϞ$~ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+E,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀[Mn'7G.Wngw砇.fK騧>.n/oSO7d2mC@g=B>~E GF}^c.ޡ ! ,P2IH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw  $,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL`:' | ZkA44[BpC-$m(La } 7 0 m*ę- p $(-pHDUp {H$ OLB $lD a&&AQ6l!!Et cm5l 9(4lK@Lx~#F b8łTЎ##B pBC9"B&D[YɈ20a'H0*W"RGIi#.l!98!(Ɂ|#)5d0jRSE+i%"A!-b m` A:_@N1IyKrR\ӟ(5bmӘT2/c Rf:'NSAkd@]PӚ b2KDeHcAX >c% *PDI"HmgRY9s@!iYLC"(2Q$!AhSP@j)F Pȕ"H%-q TvuN! v!V|f@l  JWrj$Adp#*HCe&ҚҔR@) ַ lC"dUKM$,dbYAl@6A KUj;wVz};cFBY=SAQ Sui#ѳsچ)=U@(7cq_zp=i)GNȧlBF(ES['|Q=h ؃ۨ$"aYă7컍 &Ĕ!i}I : nIҁ>`R*p,\ ˒A`@Ӣ%Mنj1g𜺻 )XI Ætg8ߏ,_*<|tY_8kakgjWȈ? FID12vŵd,C?Z\CB Ru# 4hKLYN[U5b9tp vˀ=ul22u\)VyLael~ .sl.1 ,fN)p.ֈJaCL Ih+搿i-U]-ql0+Lal*e#q]Ս[l&v ZcW)Ba;b qhBϞ2X}CV1p4Lf3_v%aixoc| ?]cTl MmY,Cy5O=*1m[|^mHB$ڐ{:{pEv a&4tf#3F-oM{N@! ,cH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-د dlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o_Eg$m$a'a Ih-lq"l B7DB|BC$&(@pCGim K0{ -J@ HpxH0z$@x> IȢ$x>"|(2ЌhL+b5pH:x̣>?)(G" ٽ FQ<6"6l{ I R۰7Hm  QAR"r,?!A$$A6HXdW Vrҗ$܀d^ϔt)rH! bxɃhÂ.Ƅ *nFD쌧~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ!&NwӞ@ PJԢk IP[ܡ*M2 ȪV*$TjղaUe(n e -$t%, #kx e1J"bU7~o| bUK&V8ʹHM YS'|R W<enBvE. KXԩ֙@Iͪ0V9{ĢH ~E,˩>Z%!l[[4jՉcPnBI*y:%tZzD݅/#S[[NC{t S Ѕ kȀB-Z`` a,c(@d0xck.8!*h7ZՆ_ _ dM *Ml X#*!1Jzg8@KۂCMnFv,I2345= deHh5!!]|nJ'C8 3k 'ı=@l_$SFdBB$|y2A 8a,)k;H`B hƆ>dV;E_nc$X/-X w V?I})ٳ1uV iƠ3=lAb C:Аmq heN( Fe=.[X"JjiLo18|L ~LƓȋOs %qsP,vp ik(@ .1"p\.55I3LD S PLek02m3Kt^T灱r'Ø#.ts7As ;S m!34@KYX, .Ћ$O:;p^ y ٯ@$iC&y-SݦYވUI)e32i_{Z2x/-P'\%3@_x_GSbLNGduoAoX)@t4v 6$v,(s'$vz~ww}ӧz{qAc?͆H4Kā'H3wYp InxU^neY߰ V`pKCHQK1ԀGHoU\ uV&Rp KPbg(  T%'gX/Z5fEUflXUkFt{q4$[$ &d {HEOH?PmmZ\uSf6vic$0]4j2Ng(@Tt$w6E@ZU`^u-PT(5aTEYT~]Bl ƇU\TP hp$k8ks*`NU7h׍q% %XDCbBhm pYW% !6o1(pL c&1` FZ>Ǧ1Z>Jvxd #D) :>)P)H9>`)ps s7}HZ>q60$p @ޣ)MuM> @*c> ZZ>yx)> ?$a /T@jFVs{ԚLǵ N.1s !z= ۰" reF࠺[euy#1zʮKt_r*-;>*@ȨtZ e0;;! ,TH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sI`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhSlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/oR_BIDD%_BI_ R/̇IB  `Ǘ7${$$P   - B$$_$hW!䂑0_B7@,z` H2hL6pH:x̣%| IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2(@2Ќ4IjZ̦6Gt|$ Nml6܀38':g$S )-Йi@THg? I4 BQσ>N7h F3z(p},T!g $\lH RA&)}"ugvZ#X*7:U I*"zTQ=0;A rp%l c!HݩD70|#f7jծک P=wa@^ KHTkQJ<}lB`X"dKuqLA ,A&)xmHOqϺ/,_" b 3e3*݁V`Du fǻܡ%d0Sٞ} ?A*[ @{B\w4H- -@Be[.Ԡh@ZQ v(tB09\₶h1B^m₫Ig[`FRp)d4a k ?*_5eUemT=in*8:HENIhǭ!n ׋y [; Dq7 9_$ d }]{dW=fw~AoW^qBUgZ4/ V8 H*͟Z&[@iKELTz34 =ОG&w 'W!s|Zi1}DJ7>m cV- moW'HmJ.?WUdn"%.kY x@y IE_sRgl)xMi[.Ajv*𽭐wd:HNA0>ٹp햟4!jlvr )XbeoL, [\cJEPw z; sm]V-'5NmVO^ ҈wïUnC`Q^lܷ@Q3/ASƕ>6}|ޞAo͕?_.}JI|O(ڽ\O_g;vJV];mACBއ=s w}ңfQA%Уd IdMY 8ށv$x-Q (,؂.028u456w:< Ѓac@8cDJ"! ,5H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPAJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-ؼMDhTjp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觟|oP H@B mDDKr~E!~j/@M ^x$|H$>`*RHbޓ` آC@ 7\|` 7O%,_ "q"{$ц6Я%0$` H2hL6pH:x̣s= IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZqGE&ψ6Mi #1rbIHCJ rȝ'AOu ~F9$z D} `$( `!nC 64 @ZmS7RmD%g Rml?҇ 2 NjZDH JXiOԂ թF-6olp`K|#jA Bәo\a386U21`Vg y/ 2$MV3 rգP*&+oLAyM }4d$A}SS~I^0 _YN}3;M:0JB^pµ)\[sUu`d ݁lV -u yFV6M8€"H؅^P^` PB[YH!Z3 o+lV-؋S[hcVg/m\ w@-C\ 8A A ZI4QƵ#kMŬfℳ!1 t!-b Iȕ_96!u7 .)or"?&e4*xɱ6S`W"tԥi?ã 4Xɖ@] AW|mk[x?N'v!di:WNĽ^͔eN6eA+R~O=Au@k]'EM CV r[i`۰ r y+ ҥhE;|bWⴃZbn,[Z'ϙw ;".bC㔎S8-q+x`BP.[8ؖVSw\rOݨ8ҘXs!HP$ E>CtǦ{\ F^@M@! ,)eH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7R=o gw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.c H$D$n@B\ %$!-nF&Nnlz%QȨF$^% QʷK~P~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LAsd6g F4%AHˍi#IBSB5u @2ԑ -+9(DGP eօ|<55xVF H(KtB5 _Bΐ*V@|t!*Rؽ^r$VЍʖ&኏b :=ݕ 9a߰kmjْpS`ko\ 7 {|S$AS+\aɅmC̛VC @`w N0e/;>'D6|)H3 M!Rb.pAp%e ?Н0$6aM%pq\_x ".T#Ge{hB q]XH8vaDX6HDrpEJ `!&C\Q$#k,Β-_")tn6f gYb Np >82BzhU #!wptC@͆HuJgY!MvrxӒ.*Y%U=$m/|AW*su # ȪT4Ѕ#|V5% ` "hvV:܂q_n Bml~v 4Qz%HKLAI-p|Ha HV* T  kh_3#^y8' 6S j<$R>K9r>`s9Be(pqE2w4A޺Gr 2S9"bXtVd\'A^ o +VNùRVdA9r(}X1C@'0<>ydVUE=DAo9!fXH 6TI` %iguoA?4GScZsinA(XTHCH3N937&YlIctJ 5 qjg3/ 1@8K GV&]: iArC5> zVs+!b6:9gCI]nrT3B$9Xp)P bG2aɕեl(G'x$T U P ~(idHndybD)z%},TI ZYeקH|CsU[)@F) HLj{Ԛ6nW$ p Jl 勄`$ [-xBgeD@-%cWqk8T$!@Q؆0 Dy-6(o dHOhXIV~?x?Ixx9GID۰2LLaIH\rb n@ 9U#|W ?ܠ%∿[LnqIQ,GdD$B' ĕnpb9YF_&Dӆ2%$SnEAknRͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZX0ծz` XJֲi*lLhct'2H$hѢl"]bkzrR3;M/H؆Z& XwGR, ݄7;ddKXICy$Pٵn !#e6i0@ .(n"oH8uKٹa3LЦWxgx(*5D\ 9A oA  V?e x;(*`ZԸg M m Rs۰Wmi;9arO0>FG$,AMNM\~_wP7 <#[i&:Fpk.% p+  ^"FW=F8S nD,Dz;J%ļ ^~A `\7kpīY!2yZ3@{6p!ANH WU}VridcDW7[ckq]m3')r(8.gfMVBk}Ck&NDxbCk&ʇGuGly1L w=p% p GE8Xqs J³Hk׆#w燎†+qbXY HaEHoCHDY;8Ǖ p17]G%ED%Qg<(сv(NG(uXm Toڵ XLcfCDKx(h 5@'{u@Ky]\ ?mpD%7؆NG &DC ް ߰ _%2( CDT?8xT0nHᣁXDCXYda c w,e[H` ;DR\dO6Z1LkDE8c PxDD|i& Dk%]HneJCx`&rFxɄWh}Ll^(hPWfiLh(hiR0$rph !'h2y!' * } 2ɐz(w|)^ͷ )GxrF I.q\x(I% M)(PhyQw1q@ &Ay.j'e@|s]kGh#7:p*:q -t;:q. J 7ʩ7H: 79ڤzs Ơ3'W77ʢ7oIXwcXzz7~3xlcloآt p ` 0g:ys )shZjkǣp Cz*7znê7 1jê*aoc.)w3z]:Ѧk#K(_#,::ʩ*1p#I]X, ahI@1k7(37)0t\غ6=f}{)!W7J0qi7&$0{zclRf\ 7 兛Vg7e i 72;dZ'6JSa! ,H H(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵkՅ^ÊKٳh):J; ۷pʝK.KO˷߿l%È+^r-ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MjsͻŋWȓ+_μУKNسkνËOӫ_Ͼ˟O,a(3G& 6F(auLhfvᇬ$h(ʷW,0ƨԊ2h8C@)䐜HH&LF)TV)T#Vf%vl % d Pc""p _ty"vIbz矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&qx.FkPaQTF[v+kAru.IHkzSY,l7G,ĸKgw ,$l(,0,4l8<@-DmH'm[oN0TWض>m5 lMUI^2uzg{}&&jMtvM=Cu+Masr6G) .d9WGm-- N'畇.U+ƷT 謷NtzJx븗{;rﮦD '7)ا pBAG?UR>< d(BBL"F:UO#R@IЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgRʧO؉Ӟ"kM?SWQ PTXP-SՍ!` XJֲhMZךp\J׺xͫ^׾ `K՘R S2V9_}d'KZͬ&z hGKҚMjWKŲ=X \ۺMBX[Xk!IpC+V34ت( [S n=cJҝ$96p ͮ`܃R"~5FF]ueVEBDy b\Rf@H_J?n/ f+2n"rX$ ÏAp[$K1VK[%.1|`,8αw@Ld$|$&;9?Dyp r 2' 8]$AV'`(5Oц6 Ng$Q;Y$W,fsRѮFb_x6<>OIEvޕP L7 ]rhشآ$yU H}s7n~ 3mxl2 wԯ6P$ 4Rh-m5 ;۶ Aۋ) Ko*{{;~@n7 -r]I`79yXV+}Ǒn-$q|$܆Hp8?7/K9ї~3]+J?N[9ַ{`NhO2pNU2A^wGp.^7?ϛ3񐏼'O[򘏋3l#q;GOқ<7񮏽gO{ =gKO(?/3Џ!2?ʷr%1hʮFpk>mKz=`W ,#sGW8@Eyfi'w^~U Bqn~i>(U7uH4 a7_ukڰ~ 2i f+(UŸ Y>I8^N`c qSng2~@ )fpFv8% ɘ}a'7jV!J!).i 36kfy3`.;=jYZp Aqr`/G)je)CI}\_Hw w'V a NJjp9SfaJQfcfJx$}iN)PB 8q+B~Jfp_:3e1 a$v ):f6~ *ڀ\:W ߚ/**犈 ::#HW@{@`f pXr>7>٬b )ځA(jv։FA3Q㺩٩mjdoĀU [v .X(! 6:H1 M貍ƒM(n7q:8w_3ZZ"+x{Q ҒFH&Ke[;mE>{2~k+pjA*f籚 W)^w30C %**(VkVe{;27YzZ:K'csiƵ͓ Z {}G~4sU6Ӻ"! P$ѻv~  J39K\K|Vh+g)g2bVbf3᫛ 𢦊їnH[) gsо?sJJeH1ì4a(b]*=j( HuN u˲+aHV6nN2(eߐ)P-`4 )AhZE Q 6 4\9s<b#  ÀP`Z ĺ0L0 8L|a-p)[t^ !(|N` v 1V#kZ!ѻ,nk@"۬C˃/!.͟Uwrˀ].̻K|\` y [ ~@ʑ86lQQuŀΰTiоqZ#,B&A-MW Y 11=}gȨaj!<ʌ0%们+8劫 AʹPm0:-!p `\Le}\0t=.he\,Rg]oqa35o]z:, xmz Gr|ZRm d6fشbM|eƞZ}Y۫1п  & myMP :G8Rt!Q{S-ݷMy (Fٝ=Qv--:m0 p.{d(<y>}{|ܙ$G={ŭD( &` |,YJAQ&Uf]ấ(>6>{<>@B>D^F~HJLN5R>T^V~XZ\W1_'! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ !ѣH*]ʴӧPJJիXjʵ+W^ÊKٳh3J˶۷pʝ ,T˷߿=LÈǐ#KL˘3k̹ϠCMӨS^ͺװc˞Mڸsͻт8<ȓ+_μУKNسkνËOӫ_Ͼ˟O>Ņ0oϿ(h&Y 6F(Vhfv (b~#h("Oy0(Ph8v@)dz=iH&dom,PF)t5Ni:݂BV`T@dI~flp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸bkhlRZ&6F+mqLktZ^mHÂmR^k.f%@+YԸ￑ l' 7G,Wlgw ,$l(,0,4lsƷ|@-s F[UH5 =QK\HE-6aQ@3GkuEm X6!rwRBTho@H4\m[݅G.yWOvxV?w07J=稷;:on>^A-I.O$;k|G/|}so'>ϟo觯/o HL:'H Z z8GHB%8 W>!| g 8̡w@ XH, ,&ćN#b)ZP.z` H2hLYTQ.o\HGȡ`BUGcSKAWP@HvAi"-#IIlGgbhICv29(՗BF\i(WU8A%+gٰP>reAKZ;0aI$L2-5Ќ4IjZ̦6nz 8IrL:v~ h)ЂN6AІ:D'JъZͨF7юz HGJҒ &MJWҖ0LgJӚ8ͩNwӞ@ P-TʡiF5BԦ:PT@[RXͪVծz` XJֲ fM둬ֶh! Z:wkGɴq% 6}L^B mL` , ld.vh^ٰTV)B"8--W*9& "aʶ1`ЄJl\* UncPX"(d1I@! +ؖ7,]lQ{–Y-}3: {nKN;'L p-2{ GLhiWb0g#q/v@ l dDNdPL*[Xβ.{`ӓL2hN6pL:xγ>j3A#>F;:8.6$S:/7N7kҞu2-RԨNWVհgRd̑.?vm+n$laKzۈ_f7 n蛣[!nv,gjoC@ 6\;%6po?$цpOε䝄l6lLY%h6o׷.BD\[CDMB''Bm[8{v$-o8D-f#Ծ!;8ArEv~@Lqbj#Aۿf ̀S؅r\]*a nkXrց1y32(0lmDZ 6Kbr0z|M@8X(_W=fiHlsnwH^%x$ol)"(lA}ׂȡ%HX Ol$u.FwۗpOք%oΖk8w/@w3ن{IHXFOcqvvp 8z\mbUP A,R''lM 'H|sw`sI}s :$ll <8>hxdᶍk= j#W񧅼p=&~=r)A7}v y}s `{x  w!:;==#u0W0W0{ v6,93B {ؑp֏$Ȧx2p. θkǐwMlH}o0rHpU9og 4ו kkΘ|ʷgǦΆ{4kyugvve 3I q61_k ~np' *Θl IhB)m@nmgAǕmFin|XFHrm .=[CkPg 0VJㄴ"#R3p號3f!rC T JUY$D  "I&%J^Fr*T#!*6J0z: {N:U54g[1iCmK pKMr:ЍtKZͮvz xKMz|Kͯ~_µLN0p;'L [01\Vj[k#UHV^eBV=GcQOҘ"7lHc $܆r**jL#aD'v6o:W,S+FC<6<+ĊB"}Vsiap $k]V`UAEPq !2/΄@V*dR`xx0^@NĜռbiHY*+ Ad0j[ yul''X(.HSsNJ1)L m(xi* DgB!6qgpU^Mљem*.T`Qe)! Prڢƪi" 8Jc.z7f  'Ф2?V#Ԗm85]>*; 7Ooq %9G^7Q¸ '3z/])AOEZ7 )(`aT|%.qrF쨞m $R^-jZo\c# K s;D`*>ڢK@z3pA\4ӒiUY://@En !Y.f ۧqReU/@K~\vqj,xf!&&&|xqj&}}y{c}cTg p*RN)_'Z*P{v6}(d&e&Sd&fTq' * 5 -`jp]wT۴E^t{4P'KhnphJbe RP9POTG7USpKd`bݔ 3+&NRxn}s4D6j'kAb&ih fk{ Tdhliqsef1t3` Z>f co')*،aljv7(k3 ԧ5f,'MPX!kZx Avsøs%f% Q'b֗ch֎y g5Wp3pSedtM!XU(hc<&NpFZS+}҃BU~Mnd vhN]c()(O 6zB"Mw -Y+eRpp qєXdWP[% |ɗx&afTTPGdFy%NQ Y(qf[(fuYM^]RoAh~FZiFUg`Tthe1Qd@ B6Y&5'fhcVVb{A,!e:EPnWRdy.0(NDp;VMJ2*T܉li@W>lIsG`*@r@D}Wy'ig&y(ԩ' abqPp8!G;tP M(E)'pPf mzM븎r P) vPfIqKpz6A[\"\'Nԗ\Mirf\&UWkZlC:[S9BiC\w7\I{٥ąQp^Z\Nꌆ:Z1Z5i呅v *M bz.@?hsTj6j*\F@b9;\By p z U! ,eH*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPsJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻW ȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhI@Sp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯d_BIDD%_BqC$?!ApA X@- n?/H @I , 8Ƚd@V(1<pBAIa 6Tp|D#aᏃ%8!61HpC>&[_H2hL6pH:x̣hd %!„l=6(&FO@A @nІl!"D46'=È\H) SB&qK’6lmIBaҘm&[<1 I`Ӛ,KPs*@az %AYmܳ8d#q=n 4BІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz-l!JBHhZLp@ "`4H\YiRƄkIp{D+ig>S߈nt `2E`]XB rp.1]#!- TK馠((d)ξW 3ȯ ;+l ?ZBC9B i=] { K30_ K0Dڀ_DK#M\@-(\ꖊKx2‡g)6_n ?GaNݶ-%nMx)pX#Ș߯Ʉ}F.u!F,P#Z-u/ta)jo#vL`  \rmgFFz? Ly92gq[@jiG7y^h_ ?oM8zh~'gW[cE~o`GjPFjk]I iGkl %[n`\yqrDWdLAdY (px]g ~-_kkYVfhWFdj}fCH Ă*Kz'$ Pm0* 'x{vf\fkX&VJ@ '3e'f"|qwPF$Wz dQW@B1Y•a7&m C7%aEOd CxxhGP|k [FVv. Fe+Q]m( &Levkb&XHpYaZQу/ @*b'QW&W`a3 w Č5`eg{-)0uo' g&y(_ WrODzfVJщagbw&ApۘvV^2aϤ ?x ĎZeK5On@ Q"o.*Џ%Aj.9KYww0Ww XxM|V@ic݇xD(hY~츁L8zFXghhb*UƖa[&o(J'o|Iܵ!H 8s()@p&Ml.T߰ )0&fJ5DY9VJUL}HEݨg)) 'U`ĤHM3iMX ٜז!у~t9&l|5bfNGrU{5Lߐxpr`aM]V [pXx jM4`Pex=b-A{N'ydL99`c՛YJ %!&hHDJf8'lu WEAČ~=zgb& _Im&'wUZ<)hhKHxܴ )'BoYn1[[Jb.~fkuonbH(d0@4 RJD:h %` w j 6eál,.bg-z9^Ƀ)exZjg~'ڥ"R~&ހL%11׫*j0hII0L :{j:*H -:c8Iŭ;p㓽=ĴNj>UJ Tx >C8>Bߐ% I0DU(>VGS]XqJ>a5(ˣpSYAg6SUH M+֍.0䓥>l ͙>a`J ʊ>lu cZF v s#r%{S Ys  aM =g~MCy $F+>r6_6R[>XT=8mP3` lP ۶ > ɤ#e#[8;! ,dH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWm5 \5h!`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/WogwW% 1Q mDI!I$I/$'a-#Q~n OE >Hp 8` W$Ox$A"`_B&(/m`PIL0y" `Q|+A$n}kCЧ0 G*ZX̢.z` H2hLFlz]H̽x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2fsC$$ I6E-MmwІ BIS+gƙ#:a؆-9.&aP{n#L*?S! U6KQT>2҂4 =ͧM|FDDagOiFE*Smtu6o"E6/%07t!9Hx#S1Ϩ@DbCT " 8jGm⴩$hL`6Bt+_A 30[g`_8VSW%u G@IPt lq*dmEjj m"1 KY{n3m;9\֡$fӆ C*2^y*$ļgo* y ^m _WJw[ `HigUA  ];T'2xT{W0-B< B6!l d} X 6CIMxI"+׷s=~APHysԆ6 `]?R*~&@5܄LV/X3ZnϡM  S>|լC6!554~z8 p@ hY t"S@f ;x ACCek^zGu؅ukOܱJ5"vZwڸ~<w؆u5JMg+[C%cv-Ċ o#\UPӞWn!qz{BuL kSD0 ܴ8^Ӆ~v݆lC*0 eej 'sjal@5ÿ=^_$~jh@+ ie65:kNl)@GOқ^V?WzDgO{1Ͻw+ewRSUL ! ,bH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\wO$|-6phg9Qlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o/I ae/~ $F$E `>$nE ( nh"ᆁo|J`(A"Ib_` HDR$[` "!sm!l#A_>R}!`.z` H2hL6p,6̧ uAQ zԣJR{K|)0+٣ 1#l24%H e lK>R``"hg;I7HFOGڴσIv >=KRڮK+T6t > rn;DwG߳ducu{K"؝E5zKsST5hO:)TG^ú5:긍]ۀ|׭P[{GG@ndj =Oʸ QK>ePĪ+X1;\[( 鴫k % N`óv6u߁RCztK=4 P #!vpvc`.ޓHXzM`%^ ({G=~3s=E+ҕN0! ,}cH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͑nɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4lx ~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWr0LgJӚ8ͩNɝdP}R K /\ȪV' 'm8e.X}C q^ڪ g@A 61!p}2Mk UrC6j @4.$>,HRYH :6W# FKDl܆1"@ P 20.ȉePlB% &-,! |Ո 'Tw%/طk[]TP5Z#pu li5fUMI{ၐ8cuea@(q@n I_mOJ6rA `(|šD@,aT|F$4me@)ܘNٝMgVyAϾ@-pB "^h3MO$$;l} $`4b+;]pj vAXߪ F4b,[I֩jhxÇ )Uc&H0&nMb'`5fy&-.\̳n')Xp\}3;4WRPL [p5ܰwshI|fԆ"-w͕dVCWbn۳mW[\ Haǖ\ s0s]")ۥy rVtpsuuZ0ǵmyJ}w X7)P65! ,bH*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ZӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8 @-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.촟B^SDPϊߺ @BnHF-Dآ7- 0~Gr} .ϷNS_7$a>AGx/-mTBKތ׼[ "!<HHl1?~ @JЂMBІ:D'JъZͨF7юz HGJҒHJOR2iC\- X74fqNwS$!uSNGm7,wԏpsGBNS9(NI6L1ɩ4Ǧƙ>\`Y!6$&$$5AH6WDb r g"Dy, [!/ص[Ƅ`6dYIܡ!wSRP3s@q%eO /D^9d1EcQq*N1.`1䁜<ܘ7J' qwvT[iG [%#B`v`Io/XC r{dM=[ ::%e&8%voRvDr:ÓPtd7^ Cqg=M̡8}m kkXH",v-%N$P.hB' gߗw;rFKxZ{58 Kq\&82ّO_7F˸7r(Y-Jcc?ޒpRa'8N hh4B)ssZ! ,/QH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7GWw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.nynx I$Imr mF$'qr@ *#!-n''-NmDb9#$H4H;/Pn܆$Iꑴɜ#D{$(`I^B `~/FJL7 HL:L?Zcy̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2hL61N)x_ O1.0'f 9q4{4\ Y[lf>KW$fyĖl d /!9倛24`dn )P"䔈dF|?I5T:oDJ- $1IRtQ)y&̚6nzU7ǙK%LI"'zŜ2{`i~ڥ h3+ ! ,TH*\ȰÇ#Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,(@J 8-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.˭sY%!xmHI IIbH{MBԒAMH$̳@}} !q$! D͑}=B% ywFIB $EO wB k-ܰ3oo+A$n1 xkCz'B cq0 gH8̡w@ H"@8&:PH*ZX̢.z` H2hL6pH:x̣> IBL"F:wI|$'IJZ̤"YN v@S򕦼,g9˹|-B˶*?V lH.͆̋mD(ʰyƌ4 KE#[5Yjzm4g9yaҕ߈9S6ܚ\"Ap Sc0[Qj-Ig]nf헸4w)tB!֔{OV'O;f6f/J V/ՆYz1Ca[nX0#8αw@/u!HN&;PZЦ! ,H H(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵkՅ^ÊKٳh):J; ۷pʝK.KO˷߿l%È+^r-ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MjsͻŋWȓ+_μУKNسkνËOӫ_Ͼ˟OϿ3h& 6`u<(Vhfᆬ ($a(,ԉ.(4ȔCh<㏜Di H6PFS#RVi%v\ % `O_i,ؙl_p9rv|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+klpt6KP]AKT6[fv+Arku.I$+G~ib/,08' 7lG,Wlgw ,$l(,0,4l8<@-D ҵqo8ӏ]kP@T-Q)e/cT6n" .iB#'-iM5QrT7܋iS؛woOKDyd6Y G ]+uB{y9wnU={n3A^./W*~Ooȇ$|7G/=N wOwI~r1/o HL:'H Z̠7z GHB#', Wh0 gH8̡\)<H"%;Č!l\Bu;(ZDDH[``|!0J|fDF;F_>8x̣>.~ IBAcZ6ő)IlqrBUƈI/UXJaiW@ʦ셑+i*5Y* og iM%0=]u$1IXi%3(eNFmۑhR?Б"9R>w:> S_pYiK\ o&~w,)A.KjS4A"2t0~)M@Hs> >z&)Z'g$ x~t<XAo xudw(fGgr2*X6Uw/B~%FBx2ya{K׀|hb(sAx(咄,Q4bAADBtr- Kxd?(Sshp~|5AxIXy!tY'8'Hd}H*~221}e~F(fhM,~z2~f7(cHhX8(7! ы+D)(hbv$q9.u'zB,F Q7=(hx((nj-cR،8h]_IG|G+7Fl؏:aƌ).2 a8yb֎g i؏$4s#Q(ɋAmƑs!tLa,M0ryAШ'T3v$6~Xo/(ݸfNƕ1';Y&z9Pp5u8l=H $}OY˧)IzYrY0(lؘ9WQIQ:YY>  0)i iq%g^hj Y uFepר\XiUr t Im&fJ@gݙRW#xWƌDRjX iRDF\ىof vr7P s{o|lPv gV.џΖpSBω$!Jlsir4yb9ACFoIzLYrvKƣ!MC1Z_X\ڥ^`b:dZfzhjllTڦpr:tZvzxzPO}'! ,HH(Ç#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ *!ѣH*]ʴӧPJJիXjʵT^ÊKٳh)J˶۷pʝ,T˷߿8LÈǐ#KL˘3k̹ϠCMӨS^ͺװc˞-(۸sͻhA ȓ+_μУKNسkνËOӫ_Ͼ˟O?Ͽ(heE 6F(Vhfv)$h(֔W,0P4h8*hW<@cDiHFL6P:7cTV-VfZvI|)Hhlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무jkS+ު뮼jfX"kI&6КGBezG"If+F]|[۪I+;(,l' 7G,Wlgw ,$l(,0,4jqA̛5s_R:-@W'E\XO .mB {GYM v_ ffwJ أmw} pS=5s V=ZIgyv~)(\^-轡n;x ŶXnG{?z7G_.IHYw~w/o觯/o HL:'H %ȳ7hq GX(L Wx0JcH8IrP`$4ÄpH!,&N!R)ZP .z` H2hLH,^q#H:Fv"< ,YF?RqNԅB, JI!RrM{dK>:KN M'CI!JTYVnőR$]I˚)H!AZR60aI$L2--Ќ4IjZ̦6nz 8IrL:v~5Q3JЇ &,BІ:D'JъZͨF7юz i *Ғ.&MJWҖ0LgJӚ8ͩNwӞ@ P%tʡGiF-NBԦ:PT[RXͪVծz` XJֲ+*Z!C KZkgHVHr̯IBH(j^-$7D%H`6 HD\0YƲU rY623$B൴$CDZ$ m}*Z zgkXDb- lX$<6q"1Es% hZ% nb*Mz[ĽoAKľ7eLN 1'L [ΰ2h CL6pZT<j_LcٰUqJn@~aL"HN&;PL( Xβ.{`L2hN6p +63-=πecm !<>GF;ѐ֕#M1Ҙδ7N{ӠG}I:Ԧ6uahNҘƹO6`]iY ׵p'vk\ձiVz(e3o.8MKѾv m5vtp [`u '1_5H@uiCpw XGҸnHX֓F]Y4 iCO85%-hM_f,2s`(7 ":re G7 @! X}ծ~f wFչuv0o7g:B@oc~l4SwE3y3~_)hp`~Ɨ!҂/Gb,3_PGĴ/c$c_% ּxANLw^~7s}K ؀ XRFz؁%1c"X&xf(5F~c,VA1x'N8؃$v!B<HeNPR8TXVxXZ\؅^ r`8dXfxhjl؆Rp*ps%! ,MTH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲˋI^ʜI͛8sɳϟ8 JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衢袌6ZF*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf&v+nik^+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PGERWmXg\w`-dmhlp-tmx|߀.n'7G.Wng 砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GHzR wR0' f⤅8HbbC@4a䀂$- ,%@ IE ,8q CEr$B)IB9A(Z4IIxQ C$($$ЄJDDI%R@B< i?a$KJwH8K1reIGU䐙t%KZ)K-s^?2`L2f:Ќ4IjZش?br 8IrL:v~ @JЂMBІ:D'JъZͨF7юz HGڶ[(MJWR0]7uMhC I@i i!H6` \6& 8IP56kF"pC~UEQUHp5S `4)SI­,^V <?" U%04o ֱOA آ7Z:T[$fMT:VMj7ܢ ]H-`yެ-al$IinSQZ%a .$n xYˎ1= vA*,^Ù_7|^w f_[Ww.7%b00+WN4k+`s W0gL5Vݧ@L"HN&;PL*Vβ.{`L2hH! ,,+H*\ȰÇ @Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJ իXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|w-U'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL 'H Z̠7z GH(L W0 gH8̡w.m@ H"HL&:PH*ZX̢.z` H2hL6pH:x̣؟m IA,?F2R#)IHZ&Gt$ ER/2LVϕQeAdiLT խnU %-:&!̫DmҘiE%?撤Hґ@P$/ұTlSPI Ȧnٷ tWj@mհfZcMZָεw^MbNf;ЎMj[ζn{MrN7[p! ,TIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIL6sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXS\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o qcL:'H Z̠7z GH(L ֆpcm@$ H-[!y(nId6   w8 RI^6n)ڎh,C!~_̡H4! GCHBi%2! 81 E?n#%f2 9! T@pGZ dK!Jb+@rF&jcyH $` Jv6DLB Qی#(1N$k؆$$I(-R'ܱ̄0~ @JЂMBІ:D'JъZͨFU&! ,%$!*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8-:@-DmH'L7PG-TWmXg\w`+DPd lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L WBrHH lv.4`9Xb=}DAπ,h}1Dn3B gnBJL.-ieӠ5>iQzcWVհf7Z6r,Rcm6n`Zz {[;{7 2mzb/N$&wgHqo޵,x{0RJFf#wkqW ޻m[z?"n깱4ƞ?}mw[{f6oS/.FFg_oֱN:Ō! ,qH*\ȰÇ#JHŋ3jȱǏ C~D0-7JHIR˗.%|()JnNls·Hl fJݴaXȄI$ D[M0ׯ`ÊKٳh&4j-ǚbfs6H"FlXaP&&kJL˘3k`>۠xؤ̃!Ze;l.IΛ N+Mx[$[Ο?_$tDش6{_ErC[k*g*G/;I؆Fx&QPRdRN9!KJUڅ6HV]bh߈$h(D_Atbj-PS1RU IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:v~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz` XJֲ3$0Zֶp\J׺xͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺlgKͭnw pKMr:ЍtKZͮvz xKMz|Kͯ~LN;'L [x7{ GL(NW0V?28αw@rr 9؆@2l6&7NȐ * !ڐ6,%H= rB\[TQ.BfԈ!YN6~m3AlfCڸES&țb>M*QAX WcZ1V,mil^A4Đm$l@lc;6ܓ$@bGgdh?UfDJmzJR.% DTPjd|_SQz Ra@ } Wfj %%dI mdZ@"xm$1I QHGvS!yœdmzL$P?M{Nz6g s%p?,~DhǍJO@B8DXFxHJL؄NPR8TXVxXV}Z؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx\-0W! ,H*\ȰÇ#JHŋ3jȱǏ CIɓ(+Hɲ˗0cʜI͛8sɳϟ@ JѣH*]j0ӧPJJիXjʵׯ`ÊT;@cʝKݻxkoEO,p F|q߽K˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkν: Oӫ'O`˟O>6(4T{FVhfv ($h(,r^0(4ؕڨ<@)DiH&L6PF)TViXf\vyJ^)dihl>EDp)tix|矀Pj衈&Z6U餔Vj9^馜vc*ꨤfHU38<3L7%DmH']+/PG-TWmXg\w`-dmhlp-tmx|7,S ሓdq'qDxFd̥5.Ig.IksN~B.먇g6G. .` eX}g ~'W[Ⱥ*Aڛd^[o%?F/Hݛ`u40'H+=p d0 a|.8"(L W0 gH8̡w@ H"HL&:PH*ZX,zK"H2hL6>Gn(:x >QXHp$=q!$5 $IZ/Nz (GIRL*W)L·b%+gIZr, R\.,R&=_kNhb&E/DI ARSNIK6l#k"ʙ N7INMb 8NDS`C{g;ɰqDmG~ )Ђj AEZ&D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞU!(P1ĞDMRԦ:PTJժZXͪVծz`_X3hMZֶp\J׺xͫ^׾w4*`M" Mb:-R#KZͬf7z hGKګM I!lj﷐$µIh-LFt2-mQ*@HH73H$\vpk]jn vW.n$n!V {k8W#vڗ %pft[\ $ l$XXe'DîGl(Ne0䏃 7.H X ?@l":F&;PTQXβ|8`H "x g.H,UmX@< @I `\n+g!(0gcK6nmC7E'- DDoaQZBE  LGVʦkkm@fvn\7ٕs- H/;&{x1 i~0܌ @=7 si%,ڶxhjXFm]X`.A- t X@b cVMukc d&iWw&A #;HKfpՖ6/;`6vMCajx眄 D֢/bCOj_ s@-6\\"6.r@B6%1ka.'mS~b@hi7=zSR3a] 5YrYZP6>HvAamY嶘o{癓f}ƏZͥ/ H7wF$a=y{W1K2?)/ OC>o8}v!X1u6hxǁ$X&e'8_v.-x3bv8 5(1x!Wd:zv{3~3a\AĄŁX~!cdVZ]g$/?CH`Q9UXmt{Gi"Mpȇ;@#v(znFH=hN9V[Q~7HvpቋNJ/!XJa28v (x((q Ƙ8XҌ~kuc޸hA*ȍԈ8)h!Ud؊U<hT58ABRw B!d yMAr@n` H#U)I#GI%N) `-$Qh *$Y\**PKՏa.`.-P0.-069 (Is@ 0>IYp*5 FPh$P.-`W9!95I7I.`TNsV=kIgIG*|IZI]S"Nfdٖ.pyIyi-MP[Y9ٛxyM7 Ad֑ >giiHٗə <) Tiyʩ}`_ @meyiYnYqi - ŀҡNQ {ƙ@I E ccIY]Idi㹙,iNj9Iz٣𹍢xcl.ПyNڤ0A_6ʖTĹɜڥ\>6ӄ]SEZ)j+ڤMn*X8U89=> ߰K9%xfZFJH 9iw*SY."#cG4*R|9ꗓAs^ȘdҘXDJkII $z)S)pX`ᣢV#YGZyjY.Xf[PPVꩌs.H?kDyJYm*ګ(VZz˨>*w  Z/Xi' S9w4$zGy@sJUʩT) 1R[i r@ +NK o*2-q;0A 1xIa q ǂ9v6[*N@.{lt"k0h,6PK1~Z;/P-ijK N-J5>˖ AѶդC1 !n*ep $ СWɵ!Xb**J ѻL?- Jћ.}I%~˶S2k{f0r,{ 6S?+AC+Y{qpeS;k ᯁy.0Ki߰ } *jKKKl*ATM +-'ω3{Xp ,}[*`˻NvqJI)`+65["y $ {0ɟ*ʖgy:|0fxz6`,<kk>}dP|t|TlTFƫ)pewǭd0r0v00;ȋlƙ ! e+L/ lܡђCw̒x M +jS6,qn\<|Y̒$5mi) s<!mV!/! ґԌєqCaDcP`.Iڋ54Er@ԯ8 JVУW. Q6 "տ| 5$-8hejJ6 %Q]n o0z0s %jLTVK B-Qi9ӓ`t1#Aښ_LU1iE8aupñg)kž 0]#I Z`韰mM-RYeLqMe=|\߽Rm 쭯^* Ҹ lY,Y,ؽ 7 X]R=Sؽ :| ?])u ޥ!RY0v, ]!MΐO`!vpv1@əDWN.Qߊ2Α*lôsf!wPQS[[( +W={iRN<[[NDᄍ%{iE9Ϩ>挴|">,^>[hi~Fَ~븞뺾>^~Ȟ}>^~dr! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSN `!հc˞M۸sO} NMƓ+_^УGuسk|GwOӫ_Ͼ˟O_.(h!h 6F(Vhfv ($h(,g(4h8)DiH&L6PF)%V1NiXfGm`iVkbihH]l& tigmܩ|Y"~-&a6_U>*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf{dvFDq +!A|n/Ql0BT'@7G,Đ1\gwc,$l(,0,4l8<@-DmH'L7PG-)Ūa=\vq b$Gd5}͕]v̥Ktz,cY=%LiC >~k ]&vG1~T .o -!r 9謷n炉^쮹nZA&!.|V'bF8W+/Wo}_e3Б觯3/PyN} HL:'H Z̠7z GH(L WFd*w.+H"q*9<6&:Pb'*Z"$H#*\ #a!2fL 6NIH:x̣> uQ-"șȡ;BT IhZ$k*Iq6c I<'M\#I,`['Or3.ĄG(.RZ P$2#Kp)1IL5Mm.ߤH8q4nL:v~ @JЂMBІ:D'JъZ4*弨FQs HGJҒ(MJWҖ0LgJӚéN#ѝ@ PJԢHMRԦ:PTJժZgҦU|` XJֲj&Zֶp\J׺xͫ^׾P%HB"6dկkX $7D±C]J@Q,] [\ f›$=ІH䫣U HXIr T۳T Tn@ޢĥ-qX7ָl$Pt6m-tX]`HҰ /[7A Kͯ~LN|5+x'L [ΰ7?T &NWbxx,/l[4qab$<L"FN&;ɀD2A<*Krp.C`L2A3E<6KLgl/8$yHnZAN7bA^4F>+v6В+Hµ4FX]$Ӟ-E-VEkjtҝt/JY1paD?ڵsf:ˆ- MOі\g["F-톼~HE-nt+!^6sm L#δhYuc@n#"y >Sq3-2{  | ?Hag! g$`p:[lsmMqp*-ʞ| $@ڰrI=˭A H{>ohM羍KhsiH# j'H}?ss2r %0m4sOkF ڞm@G\@=RǼ ZXJ"cKBSϾs8ݾOS-}[!.x7Nϒ7f%~4؀a} ^ 1PqKV  PD6(h.x`$14rHg3XguqQG!VƒF}B~ @JЂMBІ:D'JъZ_3юz HGZm Ĥ(@JWҖ.iJbhS @tzSt> *NӢԝ5MfRqTcjC'i1gx*DnզrīôCTenVpIT)Ve87R`uOkT׌~miC /ňZՇDbmCd V"V}UH pⲚUJjeHF";ꐗ q-Bl!m c.^!+a">"s=pٖ`tCY?;Vղ.t\Rnku:U2Ĉ-HM"!A&خlouo+6ؖU!KV[^o0ah BX  eR J6o}Mm+^7Dd#lAuǭMB-x@8cAл D ^RYTF)cbF $6g4YG[Fr">γBJ@, H^QkJ[ .41Q^H:P㌉@H@ڐn6X:XL-e;ْ $H|$vd-nmᦤ,B& "z=2gXZ YnmD d7QhaAhEp1I R/%IX" (@X@ ZcKX6Q0IB-ҘEIh@MDKvJ%q#xNz (GIRL*WV򕰌,gIZ̥.K] 0IbL2f:Ќ4IjZ̦6nz 8IrL:v~ @JЂMBKN.D'/G lQhm"_nC%6`CSi,jGآԆ e* @$Ë&A6Ds$-`VUVE$p˦bUcUR`cH?D* AXEGzEx* Um YFI H4}YO$8kHLFc *d:̹z@ڔ eU*E7a8ن0g TmU1K~Mr:ЍtKZͮvz xKMzR4]6o! ,}<H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.WngN} n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0!V CKj x$ n$lAĥ$AIt-$ц4(HpC-0Z! %@B (V @HƦ$A;lR"!G =iGp Ib2F51cN6HBaLٓbG$%}%&QmH&yRG$(y҆U&s,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8Ir>,?vS;)Oxڳ>g @@ς 5@\C#:mDFыZ)E3:Qt 6.Җ0LgjoL-Lz˕"jAJ>DU<c cG _X>HZc &64~?[˱N6ڀ2A+d2c yul)^tȱݐD4~.A^,[ו-! '{Ś Gb-}< pITK,%e=_-i=[|#K[%4qKo785a ]؄,3Kj߸_xCƥs{IK-MRY0ޅַ%n @iz"lr+YjkȂ,}%Sm O1PzKPi,![\8"O;=򵫝 tYC M-@`ݦ-T㙆)C.PˮRT|֯s<-܆]\Foﶻ{ݾWc4  HܻLlK8n ?ZسI]ƷT@vAˮ?R=/YEO iy6wH@ڠuI(ODDTda$NsPgDFEf$E2'K7Ivq;tx0F mKDRJ͗ve-6Jb~ycDuDC~yǃvwwzhq&HGqtgqH%K 3%t\wI% }K'igaJ|R TWi}w tSTviLfF%W~gm O$ EJwD~fD78f6 8LjdS7gzWԁ](|S4d'E:Hs4ws^؂-YC@g}VKg8b8CRG8KdLUvUDCeN$lFI~J wj4Phwfnft Hi['QIWua;d vwI:`0I%jYjhz VX\kԒA츄؉'xG3{' `IG/ ?xthX\5DCTqkD|WK8~SGfLNRRVY9VHYK ort)sV$r X8~ p5 0nԕ { ni {Cd%?Vy}Ginu$P`ְ)mpDFu|$wK6umqm ߠ`SV<CFKeTt44x]gl$dPl%j$hCe wdˆKY&KBf}ԟEINAaL,g(EQ9QAдeh mpJ(FPP)SM'ء#K WJK$i5JLGV.NzeJLڤNPR:TZVzXZ\ڥ^`b:dZfzhjl? .>! ,yTH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wng9]@标.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L WBtO)`p QdC IСQlH" HP >d%8E;T ;s2 #O(RP rED5 #6 $v F4[c yRq"yFZ"(8ɛs@XN愍n<"7iʞR$hC'%Q^ 0IbL2f:Ќ4IjZ̦6 p 8IrL:v;L;s,h0ĴgC;֘s,q Mmnq2A[dYM?bkQYU vs#azc G3B؂t uuQIY9¹kC:փWIU.lC.Iw{.0)mYA1QL>־5I)h `NpP<_W/A}adm0 | ֗(M.z ҾEWK 2&T-Wx]r,Mbǭ◠ȷ4򓑌eSj "GlK-8#Hj]N0oa".)-\ {eGR?vns~E _¾_La[c"@6DR?Xc̴&}op Qe꧋i_;$y%v)AvȱvI*!gͨa;} by'7y $I3$12|=_~XKy!H\͆#w iɫ|Mˆ!*ȫIqN|6kdo!ˁ>ts~ @JЂMBІ:"D'JъZ(d H27vt%*_G[ԕI ZG u<@тhf$jx"d)/ :O @-sD5 $: "Muі<="/oDm  d D^F 6r D5"T<1FAYATf6=4!]e)pJ4 $-\bt] ͞NxjC([$" g=q%Фꏮ3RGZ:X[֭r Lz6@dnkG沷? mշk)[:I,!Sj?b bEiJi[-f^23e|,/- ΁׫^$_UpX)isi:z7&k;)X3XtD_S ũjQG9~2 *ea @)`mSV hX[;dkWRz/e'Qxγ>πMBЈN亹Gp?szgĎn YR[BP+d$ȚMGd.?i2|m9ZtZF^ ՓDVWu6<\g+R p6T9m{q e^J&!V~w̔$~W$5=c_z.d fHHIrs*xOa=M~#FGm7z-U/uY9ol"7H%rEފݚe*9sഴkc`BRҗ9mҜ猱g-vk ~G8%Aȹ j.<zwꔫf~vʍ*xz?NntIK~o'tT/9DmaS{tpCj"Koy1"![qȥ^]fR…YƹSewLf|}+߃lSW9W3NHP, ?r`s$WK** !;Ŵ">}*(Ы|v冷 w@J)I:W?1Ir*Pq*PIqi~4?8Xk߰ ( *n (PAJ@,)qP06ZNMʬJNj)Qd ZypE_0A1JNЧP:Qz*4; +EMHRYa L F;PTZHejIP覵jOR0s ʲ] PiDx5VfP^gHT"i: v0v0vi rpp~Os:+QP1t8:uq 98;. ` 3~}9`> |#t"a}ѓ0{).&o^ H2hL6pH:xL6~ @JЂMBІ:D'JъZͨF7юz laJoa8#q !~30eeR10fLu*i4ӚƔ.f&pԦTFQ:Mf2WVԮNuLXT R}2J"DY@ZH%B.a }m a$d%X:F$QɤjC -ͬ62KSp! І 1j( @)FBtah$-ֶV$5 ]mеML ܅?(C&EkIin h{4^k#QԆZ$4e ·-Hv $(!H6~ @JЂMBІ:D'JъZͨF7юn,T%.1RK(S%[p1 ĦNkӛ4P ԢAZĩ@NEQHӮի`XJֱU!FE*VWRG\*׺;WmgU(W@UM݆6*u8Id WV~]`acXt+"9_';Z Qc+&Mi"WuUm_X kY[ .G: qW>ĪnfF$$dEQnDؑP-1o{Zľ(/KCpyAoi]]$%` *#B`vI78b~!HG 8$DNƵ묏ⓜw4u!UmɶL)[X!Ld@Zlc S1E|6kV쐃GS|DQkꆝM~sO.n~&Y(/ȷ(;uR'/Wo_esӑ觯#?tZhN~~ HL:'H Z̠7z GH(L WzEdp)4w@ HD䰈H,&:H*> n"P)jC.74#רA/J~lH:x̣>DLP֦H@*jlgB+.I،MqHdR3Hp"*d1\D)KIJYɞ2$J)aۺ%O9Yę&2fR&6"kśrL:v~ @JЂMBІ:D'JQj9šzRHGJҒ(MJWҖ0LgJӚz!Nuѝ@ PJԢHMRԦ:PTJժZ9j^( ` XJֲ(Zֶp\J׺xͫ^ 2u,h_m V6%t$pC$nQ eJHɺIhM$26$!hI+J"*kႜ6!\-[Іе!=ۂ:$0rwč H`&w-ÕQ :+>,$lJIHBJ [ִYQ"k u@І /x LN;p#nΰyatxDAD ȶ]9@H|0&Pڔl;׸kݢ (3PWLCٝ@ɄPCrI 2h6͑Yۄ\:)CT2<5 4D-79FfюGB_V̡II{Ӈ4ALK2 jܶ-]wBj$!k]_uFD-Q/nX_lz ͆eAe&pK,~@`[)6m&z f sKܯn+ۇbdN3K#NqL|5_i+^KgCN(/S~=kwEY,sΌ Pnn#8"un@o#\ɬctU]5KJn-C:.TM׫|gPP-u"tBbߊ&g,ngD|Μ&īȇ'H91^v PoIaj-pͳO?QG1o&nUeO؏ۥ*?^.c %=>BrT?"׿~hT`eM~A_+Ew"}mH>~ ؀ }j|js4z\AI0`9 ^~1q @/*f=w?(&x`?<؃qJ!jg{9h9;(K/]8f6V| g01QSL qh7"u!ԣhz|W8#Ah2da ~g4[X#Q!/}uG8j#؆ׂ!(ha#!u3vFqAzhA|) #}tHV`XыV1hl8~zw| a(ާ R ڐEa@8h聐'NfNB>W#gh!h S0  iH6(w8:q|-qz5Wh~ Aѕ!uEf{xx(bad9=hy,pBkM+qzbXf d[ptuIzy3(q7oYw$|8h(2\硚"i,Q1L㘴ؙ!q .`>Ixa) GH$Q!( 2)&y!)qI_8!)_xzi(g1 WE"W)*0cGIę3I?h8uT/Q{28: J6JᦓdYa~ ٞ3"Hz80 aᛵR:]@x qqJ* >:w#v|jGFway@/0iZf9Ns-: [بr29 U<+A#As *Jj;qtY$Z$}r y+ꤜ'Ѫ1j)ʢn2d!z᭔J-aaa* z*d1Q(޷ʘʭh!Pau',&300\A(pz!J݉$j/mɪ k|WrC2P*REK&KX{a5[$)DiH&L6PF)%P1NiXfEm`UkbihH]l ti~mܩ|!~Z-*&a6^U>*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfk[vFDq + Axn/Ql6  7G,1b 5gw,$l(,0,4l8<@-DmH'L7PG} TjWK]l\;B3Q$'UmD^s6xo-.s&r v|#XUe SYkTPOtxWeCW8c0CKyf 46GZyBB `Wkn~P&`)/ȣv8W'/Wo_s˿=to觯 zeoo HL:'H Z̠7z GH(L WpIH _HJ~6̡we%> H"&FL2%:P'*Z"$&&\ a!2fLc62AH:x̣> 31-"Hȡ8BT IfY$R YI=Pɲ y?i(UIdcJ;}%.w94 &?~ @JЂMBІ:D'Jъ0jaj HGJҒ(MJWҖ0LgJӚ46Nӝ@ PJԢHMRԦ:PTJժZU*gЦUx` XJֲr&Zֶp\J׺xͫ^W`%Ie_R ,\6!k$$a$ڠVʎk-ܐ !e oІ6SjBHL l"Ķ]}{V--ke{\w I msҥH䵆} WIJ 8-,ݲm[^ !m[J6v%=P_n _L>?`4 &H2aTX07`' GLW,/0gLIYplc@1L"Y=:&K#31%S(*6.{ڃ2L2hN3(5aYp!x֌Q<πMhnЈNhѐ'MJ[Ҙδ7G/`Qs:!>WVZ/>Yk0նLsm,rd bl2fΎ-bS>ݲ T;sۯ65mW2׷HvwD;krl5ClA[^;qUMpf< Rq`uǙ퇛(wS茳0̓r΀|FuyMndF~0c~4P?\(\y^c}iBΣRhGwu"m_JSC탼+nV7{fUDdG)Ss4#O#/yA/IkxP~@Dߑσ?}SֻЬ_O{PϽao~s#'4Я72}T_ׇ󣯽s}9OOϿD{ 1aN! ,MbH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲˋI^ʜI͛8sɳϟ8 JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衢袌6ZF*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf&v+nik^+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PGERWmXg\w`-dmhlp-tmx|߀.n'7G.Wng 砇.褗n騧ꬷ.n/o'7G/Woطmw/o觯/o HL:'H Z̠7z GHЃ8OJ@5$ A [=ʄ8$A D$b(92ѡCX+5DPMQv`ؒ%vhC YE1la !%A$H9D!#OB8P MGT"dAXHH `(76$H%H%%9c 'I$'J>N12c+UYr .w^?2`L2f:Ќ4IjZT? br 8IrL:v~ @JЂMBІ:D'JъZͨF7юz HGZ[(MJW0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz`Ո JֲhMZ*"oӭ&\9̺SG1 mծ_l v6rؿc mddف"@B1mq [5m5m!II6pZIŨֶk ӚԴ5] VEj  Ur:׸{c^=f4~׸yf] 뭦u $,cGmE~iY[(Wg9[H6Y]mt w1=8?0gL8αw@in! ,\=dH*\!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ Jю$*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,WlgCwǜ%$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.nk/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L WQCPPq[⇶$CIh>-~&g@Le9̢@t2.ȢeDT[ġEUf@89Lw$#>ӈ0)ۆ)H3~Q|HFQ}c)F8b!ȔQl$AFC9ql(?3N R6DBJeohC{'`D$8#Ni mb<%m(Jh ;ۣ"4J>ӕ@g ~ @ʯb$ ! ,\dH*\!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k⦆=!¾,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/oU cMA@va 9 Rp.@vw 6A`7)<`'L! g;S d0 Pr p? .m7 8C%j Q@ * .[RaH&ʁޒEA.!+VgM1#A)q\< }E\`,c=?rԅF%B7AV\ W(`ɟ 9(R0TppW q\B .!<.f)d\&6H@ -vcRgr@p ܆9Ms' Qrb;9s'>IP~: ' "P2 @@:"ml"֨Eg:ט1gsdqǙQ yH$qf}$HQDA`m؆ efF-jC(0ș(%I !YLc!QJ%q6ǚ%QIG1IyLS2$0I3H33n6&2qFf~Әf7yNѲM$cģH>gOFlg@Kxғ $Ͷ/`6$[xR2ˤJ[L%qFJOJI#"i3YBeʚ]e $ҚE  D&ݙH[_S$@-MmD<& Zbޑ$0$f,tئKAf:n0˩b|"d#A۷JfEL%(%X/K^McA g~A)c($Mq9`^pT 6@ V3$A2cMΩBn0g' r+ĂhQn'ЀmhhB0Hh3bH8guN! ,cH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l@<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷc B`SGmm!II@;睄-'ц-\t|-_BzGB=A$cHH=%MA<}~$` 'Zآt#R> |Gz$Ƞ@7 I$7;90r&8̡w@ H"F? $uF$J!h#Uq$ mXPn6$@Ihchܖ=%cgT6GqwD[@}T^G( $P#A$"H͌I'`qr(tdܒ+I>mqH4O!aDrL2f:Ќ4IjZ̦6nz 8IrL:v~ @JЂk8>GP _8!xMBߴW4!gHAwI؅@ jI@T2o*@ \ -P@&f N@v!c[ _|êߐ[NRДLU!p,= w S՛$jͮ,f )aPRGSTni]mCFuF W@U5 -A\JҚ&BPdRW M.۾IIFz۵Ja JMI\l[UہA [ *+;|/r]@pOɖ:.`y(@Y7-nhfWm @߽_Yr%(.&,8x >OX2B)6`Igچ4txY?݂6frxxH DV[$@K#E,+ lز J.dm8rpimweB\cxY^s戤s z +D%a$9lS+\;at݀eim7>qqJPc$ęqY N5C^Ы[F$mk8Avf;[,1yP?Z~u>]bcdAaz*5x;mL@!s$ o< ȿe1Af~#5~6Rae[w)M7֘d^Z 8HKٍ@YopW>t֘qDy?&!Ȁ%?6E[<1MNY)ָ ӸBd]?`W,1Y1nzb(9a@&`.5J!2x!\3A^㢿]KNPzM?[ :Ady_M[/\pyg0YX.]:H;M5~|042d^>6|\hZ}7eq_c<~ {h/K.V>'6L[~q;"sX s@e!fDKt| w5bjwX*5:7lvmdVy -D~4Sx%a 0F#t DGĶ7-@h0hgIW qoF@'(u,OnĵڐU#4FFKb7|]U}Тi (hId sPj[X^XN p um"Q[ geDJj$J*j9VhBkRwhd㈤TlYUFDo7EabIG6sֆ?n7F.n|s<{Gh3oj\gNVK<8<'b5t 1dw) kW.R?ax r ʆ]s`KFtl!_Hs Kŗ7q r 8=Upi7BwdE7I8rus?87 G2Xt!hB`WY5! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSNJb!հc˞M۸sO% N8Ɠ+_NՑ6̣KRkbË'|ӫ_Ͼ˟OϿ'hwYg 6F(Vhfv ($h(,gd-8㎲-@)AHtKL6PF)HXf喹e`)/yIh[p)眿Ixgve枀*蠆5B衈J'N4*){餘Jf]fd6~*ꨄIꩰ-ꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫hjkn/Q!Kk,l'0Nr,0`qHLU&:PH*Za"ńh` @hLܠF!R`:k,ǁhώB" ?ƅΌA /$4r*A)JBDH.!3~ t9Iʹ`됃镩e7|R%s^eXn)1)+13I($P0u ɫlrK1$-f^بN,m,`2ۉ'rҳ3>3y'ܧ@rwMhM CNM!74B7Z(g*9JR Q(=GҊH/MLgJӚ8ͩNwӞ@ PJԢHMR/3 9yBԪʪ5PyLZ1XGn*ZֵmT3Jע Y^5h4!sHLJ4ŵ(*b4:WyaK\ v!jF&[ꏗh ͎Mjպ6a1kgۮVͭnw %#+Mr:ЍtKZͮvz=a)ضMz,|Kͯ~0iL`&~@J$D" mg 0y'M U A`gA6% nG HHnY 0, ěDq la"epNA0;.mD|T?WK$6,8vɎL2hK$7A q;`+%MBЈNh 6H 2iTz HNCӒ5ERԨNWV"w}*뱜ָ"iמֵs[56~]2m 9$SvȌn&i찔{;X7=Xݢ97nuηj1 8THN[\ 77q̦5 !9;rl<2oa !ƒr~H2[6.0imP?F]m~Hn?\gڰEmuN`;nhc˼0v p?]*9W/2yD#B [RϷ0{IDB^skR[ by{$xp(3ta=q{H7E rw=D6/}Iyw^owNϓ@^bdž 'z7|Lwlx|khHh~krg ]yca@P}h$bJwyp~Qz7wgNwlV'R0xGm@r1 muW*GR8Q|aTZ\؅^`CXh/5QЅrf`lontX"vXzNCqxHsBF\ևӇp1f}h^vyvIp `t$Dr4X_B@pEdQhC~xu% #(D̶ lwbu#$g>m a Owta7NҊS/w6g#>>VAtHp@#7HT9&:I16"QB=ѤDwGc$5C︇4$ 1+a] ISI  * $IN踒W`:pSX|&I!Q5.D9WCٔђRy##c$y;VɔAIQ挥aO'gzPWbv-9`8x&|I:ab aMjf99R dt•1)aU9]Ilyx;jYY`pYQ%dAI(HYC9 @4,Hh) 9^tNc_;1&II~7$i {(ifoٹ:zO97I(ݙz3%q*&7 M` Z6'9+:h 1&Y=s(V8Z&Iw&83 vqIM :C7-У aI.@%"at꜀h}zsJgq q*`k7ʦ%' p 2ⵥRxڡ  aPԢfpɣ08TN%ia H>gx@H|*M>!`cuڪJ4y $ɸ쪭tv B'GA%pӒ++*ʕ:vv{$+x˺A*z!]m0mimotP7vx1K{Pҗ1E@d:$4w':z=o 4JA Z"znbk~5uZ #+(au߰ ` R R` Нvr{S>tʵbwTi p30320kSFuG(l2B;ok"'k#:yr0R303;/Kˀ{$3NsX;;'y w0K{ |z/#˰{BmpR{a {'{2zqĸ˿;R |8kAsjVFg nQ 9A1[PU` wоT‰Ql'n;{t HLP(»>AWKÊA0ۼN<ު{ ‚Fv9t{'{I a/0HA/RPVw[q .%Cb<@8/hqMc3Z}lơG1g+|c )r'Lg+:\9û.И).)ϴwg25΀:1yW$vP{f IK+%J!+rQ+0 /oHav&eS&(` F#Orz:6#/1:2/1(& # n1-1MPQ7o;`1l^}˪vO7Ur0]Trb݇)jo1POp?&/VIsBN"*ϐeh*Yj\p_oĿh;Ũ_[4??_؟ڿ.?._??//%! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSN `!հc˞M۸sO} NMƓ+_^УGuسk|GwOӫ_Ͼ˟O_.(h!h 6F(Vhfv ($h(,b\ߋ4h8<DiH&L6PF)Tb5cXf\zt]`)fHhԭpibrixF|% (B 裐ueVj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+VkfI~[Kbh+R˗2kFHk#Mu G,WDlw \$l(,0,4l8<@-DmH'L7PG-TW?aUwuHc@Mt~IX[3Q\ gk\*IC=CY!.)xVеnbw<~T 3$d[>,3@fsM䡷A 6zU9nR'|'\!WoJצ}|CW~觯x@I_,kV{O HL:'H Z̠7z GH(L W002WwpY H"S!ķ5P)ZX̢k.^ (2fL#%pH:x̣>Q'lT2?򐈜#$HZ5H&b&mS 5A~҄MET#ɒƸ1}$,7YriBL,P򗽋F ^˪RffD4EiZռ6Aͼt"Q8IrL:v~ @JЂMBІ:D'JѩEqb̌z HGJҒ(MJWҖ0LgJӚx(NwӞ@ PJԢHMRԦ:PTj\R3k*h:ծz` XJVq)hMZֶp\J׺xYW$A ׸+ mD$=2~Kl!=!!ld3XIfB :ڧ(*~V%Pڵ:@H-h RHbMiQW$(nr߲ʁ$6nt%[ i-&Nxk޵8 m,|c޴ f~ ApAL$L[8&7{ aJ(Nq]!,H'X(@t0G2ǐmCol#snlmX6K49ՆцI U cV }syʾ#ch}JB@0 ߙC{Ee󄼼>t h7MGowK^JL6(Бwז㝇9m^O{1ǒBO:Ɵ^Z9z+%ԴjW{1^76GW#Nn\}STWH$+Fz˾_-4/ew%PWo|ore0ed0Uv2{yۇwf|g`7F{'eY]mp]QZa F FuFv`yH`y~p Hprsf<7q%{n qPvtJ0vpG3FzG~RPg1rxWGƁGk6m#5yK'/pv W}S(7grqqdf~Pxh|8XxQ؉%8Xh@"q`(fs7+ ghv8pfogq`6&q`愚X700w8oXCzadAHX%ķPe¥`Mx8.8E|u}-vyewx# GXw،h'/wNVI-i/bg%PŒ`5֍s?Ch&yq'x`uGi42 i|̖6)|@{ʗU\(WkI [\׍Q9)gyuh8zwH)0Y#` pMPvp ~5qm:ISxG|-g`vkB`c4 IP NPN.N\gG|''x!GLKr IJ.`6 .ȋB)7葃Vhqyiw:9i$~I ).. qZqhIXi9z$*p.0Bbg隔T6auqւG&`Y%`46iZ5HHy&y-`f>g-GkآR*& f"q^6F4cBj0g]`-TZLUV\ڥ b Pa *10ls KsYzѤQZz`։9 )@U*N!ZW ( f :7]*I jy d iӪNN* S ™FdJ]§z/@:.ŕ:V:)0J AW}!!r` Ziѩc%͊kJJ:eZڮjI `z`yȫO1jկp6r"V +-[jџq/[)[- !$%f~J\@+R0O+=+OK) wY&;y QK;.)TJ-0`6![GdƬa e xb);H][`a6OkSUK*]V wzhǭ`M0z'vV\R'u_~3LaitAh؈'+"c+aJ)ۀ{RG 'uIw;n F}G7Y6hzF';[2K/PBW8 A(t`rˇ2;-ʾAq+hv{.#M!@$hk0xhm 0r (w'tvB S03 5\ W q;)tk"É \-ۗ0R4<N,3 N +gGy*1i`,L)'aE''xܗwp6La0Mvt<&#q¾ M ńU|i0t4'߰ n| x@.z"y(N<< a{{f|,˼˷VAxrx¾0߰ 6ƖR`xoXMFk3-.aJ18 7cߠ 4 Ҽzy]8rǡ0scwmG[k&ҷʱ0BG` w Np/0f S/vd>HY5}!vs 40N vϮqW{;ԶѓRGtl!E7"'t w L@A;/0"|ԢqfNv ]X'W-{"9vg s,@ F7 ηo]rw'm߰ĄN<. e0{pŸliacy"e{"0t|?ʆ{6,q7}f˱!k%P 'iP![r3b3,Ds{ݶp ݏuBϕ}t‹[T(' Mi}R@{V߰e\B}եE`]֬{huCa:;$ptmN3`᧋V|lB$^"[ˆ r,n_uwz֥Z1d,fz, WpN`ڄgE0*"n%bx[Ƨ` t0sK}Z1sn-p[cd.B3y7ه N1ܗN-@{ 皞10AhzX|0 wN`S z%{t ֆ) uQn1K tۛ7q(ow1̚/>[Ɉr&RwW1D@Š"Yxe]vYpWF{X.7^%P`b%kn6r }P ʖ૕ 2 Lߋj4 0.huD?`8)pюc^ﲇ)`0mDu#q9*;6(rQC~/v?o\L^] ߮P_?_?_S wo$! ,IdH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ$.cʜI͛8sɳϟ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ N+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈N\6ꨌ>*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf- l+IBVxI®+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG DRWmXg\w`-dmhlp-tmx|߀.n'7G.Wn嘻ڭ@gT.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH( MR$$ ,D@$D I Ab(I-@74x$41IT- b P,bbD0&IX!A3Q!GtcI&"1` 4@$(G ?l$`Ǒ,T+ B$ xa%6Б'` s< P 4*WV򕰌,r̥.w^ 0ILm##ԥ?rL$e3m,S?ǂ\@B7( `m 0MXFS 64-H`mhC,m$󕵴EXt 6f&An,Dmē !ІcFn$"QFrl#BUZP.V%@Q[@t!@)}cT?N܆T!6t LJSDBeMl:EW {0+Y_jSDLma[ncDI؅𴰈Mb:d'KZͬf7z hG?^SbѴlSbͭnw pKMr ({o$ԆPIȡ$p8{.Ho !# $ Np PD-`7}&b髂6bXm.R/~_ʡ hww `K8 l` #-PTaΡ  Az&qR0vp Ʊy^aNa$H}@)@VJDy-p 7;vy%+$`/ c ]c(BTpA3@rԈ=;M 5i+Ku) vT6ÒM5D{`RMa$! 4u(ʑfHp*P; ]!U\>5X~X>]&kBK2 ЌHM}|=GIMRRhW _^Ё>XhM5 R@D8cK=Xh]( q MЈLт=lx=xf(Ӵ yDN0{YAnx> ut[9&'rh+A%@PJGu Vi@шVm qsOh 29AĔuW)i!N'dX9A@BOГTAnp'eIS`z) im"PY}}wM83q(85&uH6G$ P@&Ѥ |9jY9v P) @nT,z2D(М=Й2xFQ T Es&t%@q pY':EoqQy U1M)'xpO IYKnI3U! #ڡBl1ѝA rr Y@Bz'yh'Ё Ud S 3`*W eI&Qjo$BYD_/ /00S`]eO xZo":5-0qѤAuOx_:ZԨMG8XTbj mMz꧸a )i:&Cyq{葪Mazڀj$_|L`uN$WS u1u4R0 1!dt Zz֯ D) TWEG&:mVO ڙZ 饡  qfR骗:2kU3[6K7K0~%TCM0STs p WzkkWXdg \2Pf*jX˚hjVMZ)2e.טChi KA` ]ꬣ|*S`OJ۰EdS3eN!elՓMAQpr}([0J|%DIk˰XQK|WKc|TIa6G!JU|K_zN v+W؊NAJU[OcTVz! ` %0 ڡ ntrP*;Eg ;j J xLXI||L۸Ը7S@TH %(cޡrjN $r`3;0[;SkW#|jtW¯x8J [. 03`aQ{΄눦Gդ yBx Q ZM2Ч:lA;,׫ {NB]MZE@CyN35kT$ md( ~m+ Ch>$I a("!  $/hbݷ$آ H ٗ`LH2hL6pH:1v(?( ďD6 P mm iAla$N1K@ҕ`K S>$BqHX$AJIN#* RcD-iM7R ", Ё8rz8"GBahE{D5 ʁnPϑ(MJWҖ0LgJӚ8ͩNwӞ@69{iA Th o1^LyM+A.Vc~c_{QI*FrmmƒZlCe(D?NEISHY°tUl"T0ʭ?^<`i=ۭ1I!ɝjfBMH\t{}݌qױ U7SπaEAx H?2]nA>y) 1cxmBXY׶ᢓhImmk1Bx4f@m!a%,Rjl[bt]S˙ \g4AN7@J͍qjY{ q~,C_6̞&\UgN`IӠGMR Νa :oB)@\` n2[`((@ \P['@ pA ݢ0Slf.xlW[}hB\/[ث4lg76,!@ /KߠuLDs(7 ȭ\PMXmJT' @ -PTJ,pe>ك48w ) A\ЂRL_]=>%[Pl^WDN[!)_SGcS등aYJ}AHXyHm DPAћpnT >!v'j J:} %ع@\ڬhhAqG?UXL%v uaV\\ˮfRG ;.ϕ5ZFW$ˈ7@%TۦbQ?n\jcL1vI*Q_,GOH%l:yKɗqtA ?sQs Tۗއ^5nHgHKaGg~bC2@Gz7X%PUۤDtha4L~_%e WvIu]e IEh@Gy=aG88\eGEINHy04yD@LApb| av[ b ACX2>HkCu (f*WJR G\eq]!rMP^'rǖq"QGi!s@Dw G*$s1G!oGiqX @tThpr!'GLAdi9"zw=ww8>jSrH#xF߰ ( g vrf* ۠HTr r k %HE@S r0.0a-q^wlXpt8* L MnptBGn(.N88, 7n{N :Lykx.mk(r pېrg%`[wnƆ;x)X.U$: RlR0`IZyR lȘC9q (nI8t1NR ?N# qlTr`T;P {|'mYS,AC0r5)to88m.! ٖ !z68wDxIm̨r 6t&{ 'w  !x7߇O(߰ $Vᡑ#. 0` MA4.Zvp $P#qy)0 6 &7.K:*IPJ3~:(njw4%) x/!.u@1ly4Q`4$JM,hk;qr{g73tD ^y&:Y~0*1Ǵ&,ٛGqgz22P-p u q Xߠr* z'wMq y[J3cS }r F%:76H> 9`:IJ Owz*7kTQYx V>6)z=] wҒ7T)&!AOZ) tH>)l 9>7e׾2! ,mH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻNȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmٰ΅ l-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯_pTE @@psH8$!~#G6'X %Po}$@`6 K Dq ۇIP C$ `j0H*‰}HXQD P@Q}/@%H@ o L`Ek X?8x̣> IBL"F"d{GldO̤&7Nz (GIRL*WV򕰬6bIZ̥.wK m$lAI@R`LE<3YI$6$M!|fA)aef8 b[3_f)HtP \-8m` l~9yԌ?MI -~yv @P(%;Vψ*EA*B"QMm`hC9jHBQh9#!DL?JiZ:*xLLrJ4$p?HRIP(Թ>VDI0L7t jU ]Ԯ)dK1>$OBXU$<9  X0{6ĩh?tOmgj%PcڰYSbI&V4-OlD 5v3{j<+ @|m4b()Tz,5HK;N5,Qnݫ@$c@)au(cRa֘\*C3ځĕet@PmCfUaƽ̱w@L"H13[^{o|8>LftŅ'st!eM[9Y2A\Ղ c0C&;k͕Ql ;A*#8|VkCܢGi N q 4sY\+IRy!}LPK d-rMj sk'O#3Pɰ9!sjަ643&w `r9"@ۚH]j-l =;Yl#Fa"u9,{^P̩[-Ȋ ߓtdqi6]aX +^21sُ8d=g* t Rg:vaQynAlbaWt-C~ *vë fӬ;uJ7ui`P)k)Y%ӣdYØc*$,7j3/^v+⧗Eau5jEsbL\>}/7=(@b}gOet1#S` @G|H^}N8xEI? Ͼ{%7"iByO5? =1y ؀*1~2|lB8|聅!2(R2,)]4H'H1@Hp>@B8DXFxHD9NnRq{&IxP5X\؅^[rcC6>a&l8/TGg(}lAֆh'xh|؇ZW<! ,cH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸ͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-d f-]kRtmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯Ձɽnm$mHA$Ih-lѶ16 B7D}#HI~HB0n H`@mآ )a  /@>4IH-ς$Ȣ@$H" I$H?#6"nx̣> IBL?2c @9ID!h$IIP  $qɁ0 $F$q$PH8i $$ )ɂ$'Q6h#Jo7D`296B6zh$Fj6Hp"iBl:o*,gԢf*9& 0J6n %R sf(qc?\<+If5m%XKFv6d,/H*$?>)F p8)/:ڏRdV㛱5!1"(i@!^u%Hhʖj i J-P7Ph+v@LWn%VV^ 'KEc5~ȍq>7,1"Lmq`$84,K`g2?(ebQS2XQOr2۝1Ţ%w&my f%r?8Er2"lIf d$fƌAc,5l<,[,g>Bq5cfΎvV|n. 8.DH`M0\nӶ &8% M[` RPwq  'ܻwrn4 W r܅.\x K HPp !߸  "AyTN8@(7H @}SH%  )F@mQ_[eH_:V @N&)VG=5ْH$VeG'u_rgN[4Yu)jqf~Ojr*fI~ӵ|1܏]0/eeg/! _J$J4j! %` w``iJs:k;Z{۷ ҹ[=:Z`O𱥫Q\tж" Q~R:VE^5FMuRPDLcʑE"J= K'pzf7\IAV9 y SvY\{DZ!BmZشb@E^ &^!+\Oo~f㔬Y aRPv@4A rRgL Yg@z6]Qy!0VL\@pdK jI Lh+վQ vp’L8\!&v,iS}X`Cl@?8|=)gHԤ( L _Y|U-IsrdQ#˖x<! ,bH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sI`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm,mRp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯n>F/D헐D$F І@nDp Ā% 'O}H-p|Ip-6@ _GJ$) l@X(/1ͨF7юz &oZ'_:% nCT,_@(x "B[zF`z !HXL9E&̈́L`ZqM&LA kH$Uz0G VtH`_פ0u@%FBm$U|iT"ՁQ٤Y. KKXBr%.QZ խ;lP0]\A3( f`MiZ}@K dy ^ėmv[@2.PoWԷqi` 6lW yxgptPj m|cYkR5\lAdZ]پt&Xz$ķ=w0W&L= ɞF@J BT+v T[c/ri#4L=T_o]?amTxm1v/55$h ~@ i mq <9YZaLחI4A Td^?~IBf݆-e v& =852,%Ѕu Gz_EO# @ ;8˵4o!m4,eKLABr% ؅-@raL .k$=`YguUDͷh=W{ C4GqˡęͧnfL.<3<_$@^a A|7an䙸rblمh+s6VH @_ A}@ra5)-~&]`Q? ?[L7_pZ2w_a -hY`^/@[l_Ӯ1'}$/+(W F -t'忭:]ΩYFgo0U x5k_ Gj+}/@]}}7fm<0X=v^cm&r{_HeV^6S u303prBXTA`fRfhjeuvU eriP7j<o:5vn&gs^ZAscYUe8x;X wS&&t=dւYbaXKhe=sEv`^YŇc_[7=sEn[j߇R{EP`PA1>ʨ.>ⷌb8 aG!ap! ,^H*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJU$jʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μe@NسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,pN l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.WnY@Ye砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH2 W8 A$(A9$ mD$BH†9I-l1C$p3LbZ$ IT"K:G 9"9)%!AzEHGHP@HH%ODH%7VAP[ю-iHAL-nԘȖ  +I.Їmh CYN <4 BIU򕰌,gIZ̥.w^ 0Ib6 ́,S؆?X35,43 ?ܣ +2rt]ނ#oQmhÞ?hn]l2'IJQk4Kz hGKҚMjW6lgK0H kE{ qRH7BHmI) Xڅv^) 2UmEdFN}ϝ_s4!箎uEO22C޼+qBTWmдI\2M t3Y56xE&TW{Cxy} b'_m@qiDvSXQV&xVEuhiL4o{ !O;OdaҡTOPFPJ (iAfB}Wn9UGYuI@p,NLxnzc s5D[ǁSdx2cZ^tq||҉8X-lhZeq}ULAprZQh Uw5~ws{؊8IxȘʸ3m< ( (0 @>r` NxO4m w[׸Zsuuvh?Utmrx>vZ؏dυZ8[]'S S S` Y?Zx{pH^]e}C0)O8,9ƈ'ftm۶mTbsuѓۖRrv.yX9xdb.@8;J@i,k&**%ddDJc(m &9biȀFgs *@r kb`HoX~ V,NrMp1Vkom+hU.V> -/j"m W'MCKI_ReҜ%`(i Q2yiҜ?`1 p0 3iyu\`eB O /  0/*bMif s*)b]۶ p1IɞY ]6(M*T py`3p/߹i8ᚫ6r !%*jW`IFq>y`w$z.pٔWg@J.)R) #J$ ڐTR933 +Id ;*@3РLjJǕIe$ % rEVڠ}ҙ*\I :Z*3 j L<ɡ{2DR Q$*/ :B*ƪ\ڐ  v*!S:D թtE P  Rꭡax}Y` :*ڞzsy ʹzpHqZj2+a>4hWg- MTI0k۞ 2p dxiUԳVyͩY6ۓ$MMVs֡M`fYzMS0sH]xIm"\0$` (LiTt ;:,ԟo'MҟVcizZ$˓'[* 1(b`N 3uXiFP'ib&Q pw`ʻ [JO` xhfb W$u5aP`jO% |Ҝ EVˉLtUM~EfVdm%yO]6a[kJ(*+Ҧę`&H UW*~+-LoxkM˦Y%%Km(}*9Tʡ滭=sҺm D!ftA'x{+- _JXzZ9Áb )`Yܾ9lbɔ Xk,v xW$xrv˂eɹLS!fȌȎȐɒ<ɔ\ɖ|ɽrnɞɠʢ<ʤ\ʦ|ʨʪʬʮʰ˲<˴\˶|˸˺˼˾<\|Ȝ̡! ,eH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-v\ m6p!Plp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯쓊DH IDF$L$@B I-@7|# &A ,HDP 0_$Q$`}A 8&i÷‚0|D ' G ɗZ$EBEP-D_y` H2hL6pHGic XHmC $ cHBC4R?9@$r6"16dxF(mA$&  PI Zr e&]YL% )E9;dI?* - &DF. LT~0'܆? $xԆ-8yGmFbĦ& @j )RSx $ z$H%5Y[ $Dg(k1%9 "hlA' X`ABh6-Y$f1x$p(&F=xΔ۰EMYG扴PTJժZXͪVծz` XJֲhMZֶp+B>)CrM B׼ ` +q3*b;!O{&A?FV%7! $|t  vc6@N2 M S5 `lƩH6 . -&X7| &TPAo[lk5BfSRw!(xW2 Ll"v[ HA~ L  ΰ7af`t$&}c(tͦ|rC KHCbXù.B )'E0H ko$ 5HHzoM.PA $A {2+4ӬP_ $Hp2Ƴr\2?"}Ip2gLADth<R<$Ŗ]c\o#0q^I /޳Do'.8z~r,6~9[%ֳ&pwַv|}@֭@^N歏H|-IMyd"9@ .@aq.9e)*pA [@@4!KЄ6O$} *# `v;^H&aTƣ CPgy"Hޓ|ې{VIR.6]`fS_?N< ZȓB !b&GMjzU Y2p?QwXl w&Q`qR"}EwpQq]QIQ1oahFf |/mEuV8&hGyTuGG.hz''1xK'&HV cNc`~1hH0{ N?*hL4y~tK߰ W 3|S |1`z M U*O~m@Z / 308/0N K.}i'g=*D}y-EZw ؇zdM^Iao*P I`P[`*7{X}vdyG8wKS (r`yӴxuMRQ" wK7{8~x}7u@60zY Ꮭ(\Kq@tRIp > n`PgчR v8=| ID?)@a Iu s'8 v` HNQCxQ0Y?BDd i3@ NЇ9g1LZ6!N v%wByd w0w/o 'X9T YgM ɘs` 9&樒| y$OjSL)yInt Y؏ y) /Ts`Y6Ku7@~$(}xG ɇMYĂrwUYp0ypzϦ"5ۄMz&+&L% R@|*y٩O!) d$+ 9D ؛3𢢇:ް .w+׍[y{EpX+qh Y^ڢLJƔtPy+(IXȎ#Yj!EZRJhS(lrg)Hu0W0WV&EXODWzJ3{ZG=9tTU ʂYhLA1GWҊZA@rzy'yߊLZ1o-_ *QVVw \(RA)Jy5yy:(*|`Tiȃx}ԯd J1];vb@+xP%[d@8~ǀɡM?8t+U1zNls%Tw&zӕd#ev,H|>) Sj Zg,Y[ ң"#Y IB!dEa wxK.p=Ops  HI>+|Npk< E.T d݃Pb be]Jp>>aG{i1} }i8TZJqMQ! ,cH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸]ͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dLBhKg-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯! %DHI_$a I ['7D%HG& @4>قmkk[膄 RB 9݇ n I6mq o8,`$,8/XW@[ iA d#&*Hy;ݗyH2ZSSH*J 6Xg6KKbSӉc 5٩4On]k:<%pJqvq"ɍ~̹blIX.˥ Z`TPp/8E"n8>}LmD\ /ѵkF%)~l+r1`N'yO%-E4cՕHlֻmg//1eqA,q9p _]: $ng} @?t߈sے.?$[oDj?zu{.b F%qZ(CJ@LfSU ?% Lq X u*e$A4O U$D(tSuL%zXujٓJuoL0UGbDUe|56O%`_c2xftUܗ$0(eEl5M{J?>$@'1RDr Ab8O4<1jfV1E4Odc췟o?#rfvL4p'H Z̠7z GH(L W0 gH8̡w@ b'" H&:PH*Z8K;.z` c (2Bo;z UHA!p-2G̱(J Cƅ<$$ )rCF!1R"HƄ.Ԥ(ߖQ4NcJXڒ.w^ªbfQrbw%2ŷe R3 &^/])4qmbC6a̋YO%1&;4yvF̧nE H̹=M|M)ce.cUFMCP!TAђ6&M):ZĥSLgJӚ8ͩNwӞ@ PJԢHMRu1)Ɵ@T-ӥZ5Vq(J:VM8#X 4)dZJU?͊[Jם5ukV*$pR^(rEMbP23jN Zͬf7z hGKҚMjWֺq,lgKͭnw pKMrH*tOZͮv xKMz")|K",%x})~ˢ5qh€`8$h9.-ίX` $@n]Ӗ;࿲qXD!m(@bp0Arq$$b@@4 d 9nM*7/H\_.(Ȳ%RK5_nP Afid`y Rfd=ץ H̔>.>E;ѐ䏃T .H tZ @GmRcԚFWVհgָεw] "dԥKج.{dkdٶY]Q h{ʾkk냬S_/+K+Of: 5A:_K+{Hl;3~|UJ§U2'>S<*X/{_7GN(Oy{\0gn8oN!u[Fw $š-:tHSE#zN4h OpmG н'δwH݊rZN<xTTmO>!E H !hWW wb/DyM%q' sdY+΂qή!bᗤ)=6gs!V.)OFpu4{Qexm\_=G#:?^ l#^'$' DE=~QDoG' *C.X$w{Bz=":`aa1ĂM}ƗQr7z}#%(=)51!%uS)%QVX{L..4B q/-`~Yx]yp 11!z ]A?Fx%/rx44AvHwng|NpXf7@u6wvɕ~XxGh)_^㆞(I1m2L]z<B 6;ᕂGmQ6{XLx`s7hwj{3` Jl+ AXkn\!멷0DP` / ^,83R:;K0Ppsa >،ݸE ;`6 G@Ȁ͝3# <})F , 0`AּmљA ԲǭC=UM)m&֜6L|5%l,]MGb}&m1"{as :璺-cM8K֨H 7M&1%PHp\b]`Z}̞F5~S4=k;2E0m 92 +;;U+ >D ݞ<%` +Y lJzԄA5tA>f6[Qn]YsB22a*lĉi.~ cb,P_Q1~1~ Ms 30˅<]mq>}=-ݠ0ڰG+ qnϫ{0UG>"x 0W0w0lB٪,4lȦ+*j:1 * מ2/KL~$h\l\+܂/3\k ]-|n2;ӊmjDތ 0p4 7*ګGHQQH!G7uJ1P2]o v3_O2J(1~0x# xL<3tDW⚆AՀ?Kt_uR ey[!vSx}嶀a4N/g8Zotw]M-,[/oݻ/Bl]UЩЌNJAv(dGx_@AQXIF)΋o]|x :>WkM@4PB PD-^ĘQF5FRH%MDRJ-]SL5męSN=}TPEn|xTRM>UTU^ŚUV]~Vl6c͞EVZmݾW\u۶HyX`… FXbƍ?Ydʕ-_ƜYfΝ=ZhҥMFZj֭][lڵmƝ[n޽}\pōG\r͝?]tխ_Ǟ]vݽ^x͟Gdzݿ_|ǟ_~0@$@D0AdA0B 'B /0C 7CwJJ;bWdE_1FC[oF7 ! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSװc˞M۸ͻ N<&ȓ+jg:@سKlOӫ_Ͼ˟Ou7(@ 6F(Vhfv ($h(,H.(4h8ƈlH:x̣>\F2L"K"E R9#^MI$딺MF` C&er tKS)$H%#+YZF%|`%ԥ0]2JjɔLpiD+2yiR&6)!mśrL:v~ @JЂMBІ:D'J>MEǐz HGJҒ(MJWҖ0LgJӚ(NwӞ@ PJԢHMRԦ:PT =^RU3h*g:ծz` XJ־hMZֶp\J׺xͫ^*M+C@ V%b~ӱd'KZͬf7z hGKҚMjRlgKͭna۱cwlpM.oz;vǯU?tZw/νOZkj x6Mz! %H|'2_Էͯ~>}zv6;1pl [ΰ_t&H7L(NW~)6αwf1g] ٤E1w\3C~L*[Xβ.{`L2hNi61 x t>RVX\ZBE~+}(3Z4Zut I"F!MmB@$,pC@ZۺtWb i$Cҧzjm$V$HB6$N’[l1Ց$oOԳuumcM5`9sz h}1O?z5_ $ oqkh#(kn֑@4_fDߴ~6=.g!&$jz ᦵB򁬜֕ D5 zk. ӛNl{ HzE^ZܼLOwh~sx5Ar<ڠuAo@^mhw0] ƫwa 9 ׅ ? tj@/ ^3@ WE%L}uu)L`)8n;*$>r@Noep_ neopsmWz1z7 zwuaxf dj Qri10}7|32q*(7_'& Q~kGxtks!etvsq~݇z' [Fw`w#օ\avxp $`U؆'` i~ E7|20+10d:o VwcPI Fx| u psI07ާz!WXw&fJCGy0n/Fuj( qP^Pgz(8 [m _kxuȋes>}{Ȏh R Xzxk厙w8wm'xp xt,8(sW ׇXk%0]stjHxq8U P8sI SЀ8(lَoj:G'U^VeX}`(.0lnu($)oh,9].%-)pnm ؄7/NCfx%P <e fo'^R7BȘWpׇ0yu2'oIwH[ji_f`2jw9By% D9I ng y-w~ `~sB=bnߐɘ1(I  n6X^1ǘyLYHk7yP * Ϲrn6q.;BBbGʸ pY~w`p RR@zP ' _t! p3pSh_i g(y(:|Sqqow)p B#Yf@p"Cǣֆff\Yl֞Vh\B=j*ln( njbe>JjG楊zb d:Zکj]NFX*hf$)T!:ժHaev؁Z>ťZzȚʺڬ:Z֚ںڭ:l!aK! ,MkH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲˋI^ʜI͛8sɳϟ8 JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衢袌6ZF*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf&v+nik^+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PGERWmXg\w`-dmhlp-tmx|߀.n'7G.Wng 砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z G W(H mHBfX,IB$0 $!_RH$ ) mDp.!lŔP :tC'F*iC$na 78!QB$$A 0H\IC1$=Ay#XƘ$:" Gnd8GKD ݰCOXHS$<cIZڒ6*.w^ 0IbL2Lm#@9j /HB@ʅsۨ-p Q/f;Qoj}9 6o?$I^3$? z$B4hh A<1FO5h IPT,:/jhcc QDOԂ Ĕ#6~.UIjL*6$/E| 6J*ρR &)膤 $)i;8f 9MH^׾ `KMb:d'KZ^g4HURKM 6ɺ׃VqĦ-8QuSeJ^s%k2g֛ڀcZFtqy Ni3CN*fAx$iHͺhj) v?zKԁnEmAٶHBD۹u#EQEJVm0х-QWm61&yXq@Bޜ6 8V Y/v -׸DTcKXUE824.gy v9T+,~zeNTˏ[y1m(?LӁ@\jŜ<8 nܓa{*w8f Ʒh iF!lҖcmV `DcQimpG, jW ;D ƳgBȕ268=gyʙgLv|8 sd^WJ29K[ב{(A$$JwiGW -E Avm8_uY[xV³Vf^Cْ(OW!X|JϜ%o;#^ӠD$uNDo'$ EGB?* 'L7b;T7y\-YC^Ͱ%N;UϯHy!ߢi/)ށX @noC|E<*q߈֛nySѦix}Ȑ0@ oV `Rj(w bkw! $0HI:|wB(v{& gFNM| @ MF*sf}x)}7P' xiQt% ufUt\VeWbT{)e`[1b'O?`5He~NH/t ցmߠ \Ft҂ ac^OI.4~HH7?h\Mnn\єEPo.p ߠ*yR(Mf f F[ 6`SGMm$nm{W!mPm *lnphUIolڔ>mD\ 5c IPp H(x)*w}ȵQMEf$^/^|x^drQItd1"agaZuHrt~uQDWCxhڠjh j*++X"Ed%^!I|S(ueEg)]XL#pEPFzqRwu\W"n 6TcbzjZj$_:C`Ȉh)ĵ v3)..Jrchw{&'eyXvS)KшRѕ%`8=` rD/'(>JC1%3 w` NAYM ?.ЛXDlbFc1)TӗAHD`MgD9ybQ3KљOMOOIoTyse}y9- ߐYqU' \( 0NBoгQ>9 r78wyrpw:gИ !Ҵ sQts):jJWb]/w N<iD%aj,V9E!ѬJ2>ڪ4!MR'8w쳩,AO*:%H;ovM:.gV״ `8EEV裪.A9aRРCU5!y H|S(!N N YkNM`S 񌊒.` p*G#?~:ڦe5;Z7⩨4y饶XI{Ј;Z޹h9 -4K!QI NgE:Z*@zb+e *+vЈ)!ѵYߓ\ȹNZ6#,$o趝I1[N+ 5|G֖%x)KUqvd#d4HnGBi)@3*K*) =˺ t_jk$n+ŊqQ0qL>c;./1 w2eyNN4J;b O Bs[ !(;(v QZv_6%i[[ K`B\+ (- hb1 |$n#[ $\k%@)[rd|%xe$kYHx1K?suY%赺Y4_Y$q> ZَMr,pqH~o5_|Y);B\-`x ¡ʁ4_#xNLp:Z+c|Q j<^ $V)=ACL*wJI! UjMUq,$,EL*r1Enպ`fϙ6_6$lz-<`L#x . MT -dI_)I5Чܙ1 Bȣu^k\Ă#WjG!t LMOLj)r vP@ N}@A(4_8|320oVa F_IԿrpYOlwr303mMuah =WLf:MGwj=q /`W0J[IzRq{=؎S0/ N$!dT^ =o-NЈrR@8]Qq1]aDM5Q ӑ}Ы-3F3P|VgQPu?i vzPYd 0ӽF8MaU ׌ &Go Y܍^x,4.Z3 ~`tO'6~7l<zj  v- ېi<S`d'$ R䑮Ő'MVœ2ܢ - f\̌f}`$fB Ҷv>!=h`Z(p{.MPui7Z5_6֎`nmj T4U0_Gj4c9%Ej1|N\jhl %~ڧzt:vz{%Q Vlѱ` e WX:^ۅ.s[_HVyajEC9<N t} v!.dtn]NP1?Z{Y{a:TH m] YCaڄ|~'~J2MbN'G\2^dЏ\M`aEq |]} A^m>~pN! ibwh$? *߰c#ɨ^KϾ`O_tMF^PZ5ߍ1RoUfɸ;>o&?fNߩ*.UW bƥ]ӶMH"Q(@D-^ĘQF=~RH%MDq ( LSL5męSN=}J<T?%(Ў3Jbi׿mlo~VXMTeݾW\uS(6]2Ν_O$$hWdʕO~k"E̖=Z蚊4D7{$%ADZn=:P[moōGsA.ѦM$7mF-vW|x^s@r|vJLvx}i(Äb($܈(2Цج"0C #-H$E OD$7&N?R1FyKoFoo_Xr &$H#D2](ȏJ+EJ-CB]c-$L3'\(4߄3N9K:hJ:O?dM)$PC/IQG4@M6-rSRM7*@6R Ab/ˍ&H bso8dG9T('K _H9f) &;ff@g9Lhue@YeBu£GR'*Iu/;lwۻF;m}TmMXȲ|nno6O\'и+ʻp/ ! ,iH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-BmH'L7PG-TWmXg\w`-dmh7Ql6S-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯-H %/$pCIB$( $D S`[ _H@I-J9PĠ$"x> nX`/ #|6 m6H4|hh6H Z$} l?D؆-p@RH$F)HAe A?Crd!DF4(]ڂfIi+mF,#(BeBF V)68`*iF7І:D'JъZͨF7юz HGJҒC?nQ DB"Ħt I2=]YA-1J251$ZAzZ[hê|TRQ -'ՃS!ȸn\?IQ P?XHUdd<_)Xh6&UqM Gf$ֺ=RD*CYVhmZYŲLJO&;  .Qi[iF~@ZJz([eaՒҲ(? [,s-o 7kI5eTvf,A]QLl!X׳$tj0 )(Xm,a".(? ܐ6eocVƕ;İruPvWvp O Uvue\ͫ˙iE9b m \V/e,(yIB]>o"y4o(#B";2Sf:Cg/(WALf $%Sp1Gxx\ KKFO1,]}iQ݇ݓհgMZָεw^l})ˢV*=|#yUdMUiMؒ)aEoxiĭQdؐm ;3J' ?ne?he|3 CcnoCތ6` qf;_ƊԐNDnXu!Fpf?z9{+ɭ)指?$+8 UWR4JC]0/T~_Ɛ(Gji@ `Ű|̰|S߈C '}RSW?Cߘ1.rSiH,_۩2N/.aYS 6zC"!Zϴ!?N?XOsE=<Ŏg 7n\@1dI)ӑx\$+mRz&P _dRHP%$؂eLG@ b}(߯+9$EuPr sfyhh qdv?7ky#t~(m}1d v8*zH%" SAWK2j[$yс~s_'~9xLWr al WlvӴvrp7Hc8()l-t{U ) ]MM[Eߠ J.PUPqvFSNTv-a6=Ef{*]v@LT Ip PlUSNB.<vo--qCJAa $t߰OϴsX! Ka;F V(Or XaRb8RC%OM!  9DHc(]i@6Hh#&xkL[WefM6)$t0$p` 6h Ppx[xH(Mv X6 (` ^;U( M`R0Y)kI6TMI)#fgI p(%yr vUvliY sNxuXzqLgv Mx+Yf1 s`q 54^;x$')npD5q")k3AK (SR056?Wi[At w)q!3 x oyQ)ޠh$*ԑ32R@S .Wr 5JJp d |>-  )P0).)ii7 v )  AHXa}R%Pq0a9B7'P* ю(0p }Փuc*ЧNaP*9ѝ h Cy8 @) bqZM]I8|.0)w?D0M?0:>MП z7*U !eEe 7ڤ}Z9e)(˴ cʆPPHc7*N`i}* mzYAYZL釃Co0Y$ @ qPv +& H!6"*r{*)@ ԩ*)`[Qv"4N$ ĥ.hJG.) +F;Z%ɆHpIRK ^[3"Y:Yjzia+KHL$:#` 871{y($zy?DE3^IAdiɌz>{;nbĶmXMQGaVA/Bc^mi` qv!p*ק_+K'"Tk6oTXW Ɂ).`X0/0זh qEB \\-= ;WV-\[!?L$, Ȇ ӛ` 3ڥ!V x -=Gs |ZsIK>v #sӗUzpC; 퓡ĸ烏W/ƫݓT I3{ RЪ>mH븼zj*j#j|M*t>g %Y>;>?S! ,dH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-vZmq!lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯j D $$~"@ O>$(A$ O7DBH(HHK$I(|;@ O `ٗ$LЃE6 1|@h6D}H_Zt.z` H2hL6pH:x̣> IBL"F:򑐌$'IJZ̤&7NMdCIJm! QS!q%+gIZ̥.w^ 0IbaLYЌ4I[,H6nlyrjLLg:v>[T<w̧>HGpv{"@w4&`Z=pʮI;% YL GG /S$IRxn-F 2ϔ8ͩ[\Ӟs(PSxtWOLRԡ:u>B 7ՇT<5}V#eCbYER%`lPֶH/XJ׺x=[YѼ Ja.mP#4R (_! ,cH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-d$lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯gP$I/P$-h~ $-(J _ Ip AІa B %L BJ @X ["!IBPHBex o}H@',P_H2hL6pH:xt6}ǂczÁCh-H! gmhC('$\Rn@B>D[$ۆ-Vm/ 4D NzkA'- sz("-EMn.mѿr cqIH-vhH R ,A'Hoj((FE,b$9L~kPmFϋ{>G[谤hWҖ0LgJӚ8ͩNwӞ@ PJԢ!T)՟T.Oxo8$E`{K6@sT?utd!gVrJ$,wϱBp(xsa=Ol#:>[8 IYO6  BVHj|DiG^ґYkSgKͭnw pKȍh"ۥ[.B˃x@"e/Vk B|vxIwP˶A) av{' 6p=[QL€ =xou в `MY^7x]' B$xfVJ! b0]CzTk1CL, T" dʥXx=VB`~H\a5J $ȓFip2ͥ"#Hr0v0V@:9#u* a W-1ް q/0zNPӏ/ \u-ohR1DX泜΁naI0+GuaC.4c7DWtSOI(hr 1rx>>զ0JŔ0y /yv0vp )Ur uoI0PݱrYi.1G/8a }65>H2Jq#ZFt0ǖJ(?3y3j#ZФ @>FMaSOk#N>SvⓦQZwI=ӧx@ərQzw >ngÐ>) U~cx`=h}*d6i>&VW~rZYT GX]S=k(#Yzi4C=j;! ,ZH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8s I`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm6=lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/oJ>B$ @H~ $'цHbKCB$!Bmj! I( `7H ID'$  B@'H@$ 4XDb|'}8 ' W~s@YH2hL6pH:Ch/AlA %~Ğ>& _+p,"цCjO 菒 sA$h!$*IB!<,Ä rZh aW$ǁ2!۰?<"̦6nz 8IrL:v~ h IЂMBІ:D'JъZͨF7юz HGJҒɘ4JWҖ"AR ӚR bA!kt/ $ 񋁸@߸ .A!M(6 SA)`.gC8@ Q odWXIHAR9!iuAU32W2hR&A#V6%7n5M8rVg lY"@ pl.! ?(2E.pkMr)\H%)6#dA4PR4՛ 4a_A$1f|")%;Dr ꄂ-umb3ipP9nqqVh`EQX좳( V` N ܥT $ *pP3).B _ؿ) rV,t5ѷe浵# 03 1(%_6j+Gd q>πMBЈYg% wi}c)`뢵ʉ[CMRԨNWVհgMZҹ@=9$h 9AG^_ h!}hk컀! ,H*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ZӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8EQ<3N,DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.p @BзēPBI; rз d< 佳$a ]@цMQ}|HHr>rD@^|'&Tb{[%R@z|C-y[R 9z% IBL"F:򑐌$'IJZ̤&7Nz (GIR*WV򕰌,gIZ̥.w^"0 ! ,dIH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfm[fk覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/#B{-i ڸ 6jK?T6ٳ D!QBcj$$_6۳O:=AnC/Tp2DH @NC`CMpT-4It/ ${pᤴѽyD:(4SC blA=0"47?@hH}$!A-eJD78Ј ! 3E߸%0;Xa>կ f;l)Á܂zD <  p0ST-Ł|2@H,0 LF) kɂ /f)!X gDR!Nl)GԓxhoX3B>o3 Br>P[̆46g`± <(|a %@L !/-u(NRB^OP?H@kRk6t1>Td;` !O>g[r "-H,KL0Jm0@gDS<#HdvCH:QWL=|m1 T4VjJ%by̓X$l! "a'EAl- mr W^քN kЫV"ʬ2B޸T-z^ƨ|Kͯ~LNU5 % 7H‰ mh 7l bܨWf '[$:C7T SXÔ893"0Ht)Z1nx7D664 31ʹ{z2cCs @ \b2Le$\$3 ' ff E 6Lm3g"fEOY<r RJy"`䢔O/Y'6jA>7,TG 1.]g9c 7oǠ\ME.t198Ar؄_\Q˹(kh5)`7]k|lc=1$7l)<`/g08an@O| 0\/ b_8~&Bwrj-,g`pAr1_MMn® F\V:RC[` W=)8Gqj'(?>a:mU6]Z6RC.+`:A hC;g[МP@; E%r6.oOͿE7x&jH>9z}Z$åN%HqhO ~T Zbd̾ 8UwP ;ļ54?pV~~ % s\hq >)"~ ETa ARWc[cx6|q|W(p a NPx7}'S}dR;6OV}GV]ev|GrA"0rz:(b7vHdnNٗ}E@ƀtTo%d LtH~?xuG~GX~4$)hOBߠ p6p=GWC6nqhn!Y%C@=e.(*P#IgWG`* u?\$[@UW$YTwx4d$vSr CI MߐD@xtTg DGXZ4t\|X(dgx˦- % OgeiFwvg @ **@PUh:o(~O8Abgjdq..'vE Dy4-Cq0EH f[[D4c`U[27"(o"(sfhZ63vv| Wp3pS 6DeVfs2(baaM=v6s 9gI.gΥqHLU&:PH*Za"ńh` @hLܠF!RHGBu \H7.tsct@Aȏ !EȌ_IJZ%$&7)BMr (GIRIcPTVyr%,]Y RA- F@n]A.uamC0TƼ I$%%5mnF rbLTS|82ϚTt&)Ffg<w(glA P쳡B2QT4ͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMԦ:SJժZXͪVծz` XJֲhMZV2p\J׺xͫ^׾ `K*T^2V4Hxd'KZ֤ͬz hGKҚMjW־m|H7H ,be%MBl1qmnhSrB7DsW@In"$1]wpnjn K,n$i\ } D pK(7q -ܐK"PH| 0v[cL8αw@*=+<&?0eC!\Zs ?8\r(6ԻeZݙqfې sJZ [@IpC b#)VW2q5R FL:;z&rN 6\:~Lú!Uǖr.Y@ \7ԧN`-I3[Цۦ2:*MnCN EpN7v <;N5M:"NOa5qĤ|gS92H< aՆ$JwCbr\ ?W.m w.͇x&nqr2U[N@نu ,QYtVzأYYSth$Q?U| yj+=0孅<&ܒ x|ڹY@n{H:}0)dHԽwN ݫ7.CfLJ0i]Pp|]$)6w4lBLߝ;I4o^ 1r}ї~B:y'$8 wxleWevZ58t asy1e77eIvt'g jf T{r}u1|W7|ryq_5vaM&fM{vk' & YE~W~ULwQT6>1_%᷅l\[wiWlwrf xv@2GUesqqU7tl8X"GrTfrrƀ4gqfeׇ8 GXxy؉a1x#!gHrhv~8hXoPCUeWq>ah8CGmI2iw/H6טCݸxmR\2e70ψt8H$v"B32؎yR7&|f^m=S% 1\7-Y成+$a7"-vq$/ 6h0w6HPL1V)irӴₒXqX>fb/&)6iTU& fM 58)P @*sh\q̘Z8ߠ J*M)zioOiд) p i Y*&||) M .*N0oyrq %N@ )l9c)~? `aE)I9ɩ7N")zE!?@w9ZSS!lYZ0rP R (Te)))˙YX7I艞᝗IC2YY5 arf&7K 60c 8d,'69Y$,iYbQ虝 .ɤ</i1`b*iڡ4"p ;kZ^Z:g aEG q(a lK iXc3)}m( 1qs7IX*z 7IiZwj (0_i8i?}*` 'sT2Izy:Y6%q|j aw|jA Mڪ iUZ RZj7+1O} @N *;J90Z%ަ-.S@\ްz~~:zdNWQhAOi!V7Zx4Xh(G[! qd!H^")c B+w 0j0UNRg6~:Oi;!8ip۠x4(y(Wf%gvL ?Iyʴg N/ /ӳ1~lI '}Ay_E?MwVmӡQM,[;ݡewx0 .%@'aᶰ^շgl??Aotgؗ"z2qѕRn(WHb0׿^O_fwgʋ7eH`,1h 5(']|%@j.S p ry\4v^uG1E-"A! ,B >\D lQ#=~RH%MDY E+o2^PۜHPpo+$OiRM_JU~uVm $ۦj @V$Hj\u횔CՁ%:T REW Eobƍ?lKn~0eʋ71ho+6;_ښ p6Ame @@V@6)A[%t#u[9ù˝?" l]vݽtv9)7A-ɹ9r[?pڵ*Fۦ,ic0B!B #K%,l~ل\pTxM/ 6.ÇtıGKAzG2dɑ1%;H\+44)ٞFdM7|%; N3O= 3_Bfh4'v%C(slqOK/4=дSO' Qmb )B Kn2񐘱V?5W]r0Prh]%T5 [y嗧|Ιz#'h 7Ir'?}f}}߇dq7̅`h']`O`ԾM` k!B(8ҍGLB. e&Yp<|9JD'w^A@ Ow}"Dx,(b8FgD{U4эc#'39ަF>яHB2t"2H1&,]Nҫ!vd&5IN~e(E9JRr$m0e*UJVҕJA$JZҖe.uK^җf09LbӘDf2Lf6әτf49MjVӚZf6Mnvӛg89NrӜDg:IKӝg<9OzӞg>O~ӟh@:PԠEhBP6ԡu#M"1䓽 ! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSN `!հc˞M۸sO} NMƓ+_^УGuسk|GwOӫ_Ͼ˟O_.(h!h 6F(Vhfv ($h(,b\ߋ4h8<DiH&L6PF)Tb5cXf\zt]`)fHhԭpibrixF|% (B 裐ueVj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+VkfI~[Kbh+R˗2kFHk#Mu G,WDlw \$l(,0,4l8<@-DmH'L7PG-TW?aUwuHc@Mt~IX[3Q\ gk\*IC=CY!.)xVеnbw<~T 3$d[>,3@fsM䡷A 6zU9nR'|'\!WoJצ}|CW~觯x@I_,kV{O HL:'H Z̠7z GH(L W002WwpY H"S!ķ5P)ZX̢k.^ (2fL#%pH:x̣>Q'lT2?򐈜#$HZ5H&b&mS 5A~҄MET#ɒƸ1}$,7YriBL,P򗽋F ^˪RffD4EiZռ6Aͼt"Q8IrL:v~ @JP_ѯMBІ:D'JъfkEbLz HGJҒ(MJWҖ0LgJӚ(NwӞ@ PJԢHMRԦ:PT\R3kjh:ծz` XJy)hMZֶp\J׺xYW ID`վ`%ؓ *ְw) *D uA$l1I/~u o[l$V.EP$`$V9 umY46LduT D=c@[emDӚHBsX6UKk X [7A m@667ͯ~LN;2}pVl! [ΰ7{X-?>lH^#>W'cvZ[C'ƭ#%%鱏L"yy7>&;Pd 2eTy"WH.{I1LlhN6L:xγ>πMB̆NG0ѐ'M@B8DXFxHJL؄NR46Vx"A4.ah6D7 =dX/=gek8#K@ȅѢc7Æ1`adP ؕcjt!f8^\HpK2C}a[r ޶YBh%y-m]BI^W 5boJ؈eD"$XlڵsmXF `"z(T֊IC2bx' mgbKnjkuchthj@#|vNj ?'n8ftv Ǒ^r˗Ɉeq%ĐWvl f?)iEY또(IdXn R90>Џs'Q6e` r N0`嘉hɕ5c\Tvt W 3zWp7hji4 A%#'|ߠ / 30SiPCe w/ /2 K|'n84M 2Py`) Gze)k@{) \ 91 HC9Aml|'g}PV6Ra1r(HmAƥz!{h+zexɚI É0p3 lJ)&Yope& ېy6rIj2J#x;$7 !nFNZ~mXjjʟ1iPLWa )c߰HjFpxזo˖7e#g(o' Rp'6p rP93.Wߠi ` khlYj1]7l\grqcjk bf S0wjZbzVu}wx*cd7ew#W6l0j&J9HjZw*ӵl9rzP\5ifJcp}SpvI[Ϧr-wZiu @9. %n` ).p Qm|:jP l0Yb:KPh% s$air"zops.um`&rJ([%=y;!X0SFZ*opR6ׯ@Kpgv x{'nlj[%h}+*i\Jߠ.fʉtkskq))I 0 2R`*` xXFwcJԩyRio(Wf1T$[ڠ鼒j)  ׍˽ovX ,鞍sjp eU zН w ҵػ"Qx^EsI r?K9'☮hU2h1& umk'F)mo;,co=afnc˃etg0nyu|`qL2‹!}\Wx{s<+\%NPDAz\,ɘc|ɞPUi<ɟ@iʰ˲<˴\˶|˸˺˼˾+\|ȜʼΌ)|l$! ,,cH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ$.cʜI͛8sɳO~ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|=蠄:&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6jBVkf $vm||jk覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-Dm4UL7PG-TWmXg\w`-dmhlp-tmx|߀.n'⌓7@7.IOngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠BhJ@ %I-l16H 0AaH\h$ a AZ'$v86Ї!H 8H yD0 # C @AHDILAHD4BH(0nL<"ܪ(?1"7$ _xH+ mXaH +)1Q(GIRL*WV򕰌,gIZ̥!^A|)_c@f%` bĆ$І/k' `m5hÙ-$Am|%h6!'j# P&8eHHF[DŽ#c?9f9} haRЌ@(A3h !Ma g{t ?>ԗn@J}hOsӗԆ9{[`6cF,H?4a SF$T@SMjrKh$mb. zsͫ^׾ `KMb:d'Kٯ$XV43%ATZ6tF\MzLĵU-nZ| Dô)W8ݩc>zLղ6Kpn\r)\6Wu+ 1K^A(dj%Y{27U" I @h{ej85 piW 4b.lrNFftD'IpPE [u+oc)I0<P]{p 0%j$HY8m=A`<[V&"fza:гJY Ё[J>[$P_&#1NP6.(Y3"Cb hWh4Hgso6%)N|mXg 24y 9~ 7m[^>%t٘ka,<+`i&S%p3Ŵ&c62jְ-i)&jWXZ;hV D81@jϴflڑ6ds ]W(ַ* pڂ^if\ZUe)DΒ~0GN(OW0}UHteP_z׭Ng*7Ԫ}}6|תM2ܮSasz:ͭ滂*Az}lETpְ])B%rޖ朗Z;DE7\1=XGmWc^3!g9_(o#Jo SIs]ʫ)8fS'1;9}I9NR ,o|i{ bk׎Ր@>SW0vt)@?VSN hR[%N2 /&͓ڰb]Uw@5`Em_I$`wSzaVfzqhE$B47 tkgvH1*,Hec9Q9(*iv?t4N  AA*@N 3*t \7NE&)M.@B*@-).+%UPP&`Rp -P-PqhJr@k N.  ЇMPd. ppXߐ*G8s@f(n(r` ~tZ.8FHXH`p(،UuP@p樌n M@nq!(rPE{@Hp* F$(X.>ĉ$ FS)05AIK;R!89g )UᑳÉX8 '7 s.0h  ڰ (U ]É}q^ kI`pxP-p ڰ0:S[iЄh1pGN` pɕY);HIMDDuF?񏻰Lp(t)p(Sr@.Q])éQc(pNfNh©*`w)H4|۠Q19S NcHa;N0 dXfX~ӐE 5 .@X1~(`C/A_:AaEa:1q4O5`q'9.(:UH2 * ǝ(q q@BN :y'zH 3Qgy'j 9:%y W'Z5Vjlj)M( yC 1(qeqQvJ4 9Ȁ7[+$ ,ă>bLRVpQ M|& $AKCx<$ <*CO ޡ3- $R. ` qdlƁLhEE&nFAhQ+;W2ŪO ,\ pT(b5,`[ qLCQ( fѭTa{/lՂB28}: wɼVd R:h..:FV D-y, 84BL5 \ a T/ %~&D Tx}gY,gq)fn?G}+sh* z~?MdI@ 9ӏ%(vHt?oARim?]H7{Ҁp6/҄p'>̋FR (4^ML!0 ` 7j?k[i P ʿP?!G}c!n6 NP;!rG~W}'&rfg.~qV~rs.@3Fgq&Pr6e*@CWwaCsQsᧀ .~.=A={3%8kZc L=8- ! NNm$Fh4 wH V|J`(ptEH9= N0,a9؉IIj9Gȡ2!D=$ʇHwAH3.8! jD`jI{g eÄXfvtXԟשsAuvC*ڟe cT-9 9 J߀YwʇYt[(f{sgS)0ޙx \@Xd]uz~tb97N` H:NZ.P[ړ ]ZԇX7%XPg 4H@X8rdNpTy3؟Y^S`v@N!F)k1#>1[z@SK8&Cwt`5Wx`)B @1x% `b.ӟ:HocK29H[)Է8Om5C;An$ ^#3HSy)M2*Woq42d>qM2::Kj (0NԚ6κH8O)sW`+D:6 ѳ&sZZ;l,M0ű%0cHr=9qS; 2tKzR2/7HBj/5 x:(c'ӓ* 6Yq:R1@/+V"I!# FNk04Evqg/0/B͑LH0n>mkEH0ٺHްqe0JNj|qP{>H(UM 0JH /n TK[>)C=|rdV= 3vU+e>[uIaDYLʃYNK(p "HÛ3(|>@C$@VK%!l>ZAa\>4Hr>iv3> =D؃a9H,c=uAR nTYw̫1$$A-$цP$|D  BXI" <ͷA }_AJ Ip ǗHσH$WA ?[,0 !K?}X"ChL6pH:x̣d ن? @~O+ |n@6ICDI G! ym|dn%$HTz%X'P `T^ @H/;Iafn-@Rݓma?$! 7’ن-v~ @JЂMІFD'JъZͨF7юz HGJҒ(MJW҃ R D>$ w !XEJ@4}E @85h ԧ $ MP @og*-bK'[xT|2$bW0@U8Q@Z4*ƬSūB+ KC4ؽ9.=cOVMXhV8A.P^z8` mڄU蛃P !*pm1; c.sKKD>{վFxKoo &I+ M8Dm;㢀?TV#. ` -(09E8 ߤ~Az8oM"ق$ (fO/xњ$A 79XiLvaRYaٜl$Sm;U$5T,ĵMH|c_7!4w`HXc#S}sL&ۦ^+W:VijQd*<;dѮ2U"CA+9W2e%Jk;IXsL^D4N:|6+Q@ja)Vle/@)H7]a9A@l1Vo`:Ib ! ,bH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dIBhSgv-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯_n mOv B J$؏m x69~@K/n_1?;;tE 6O rE@X!A#$"B~҄J`pD PĂ0~(P%R C*F[a B, 8C`91!u> IBL"F:򑐌$$1\@2Nz (GIRL*WVox,gIZ̥.w9(y K>6$ sl&|)ba m 73m؂,-pCh'AN! 41Kf%Igß 7MdDB" `(P,G$ hҏ8 QS!Te|If i`Let6QS ,%Y7AАj#FvjI9!N~u؆J bɳ=tmX+ d˟vTR}HY$Ӆu-@J$7l?$T7р&O W"s\cԅG lzռfTe ֻ h5~sb RPv-(ϽpI،qD,AQ_2vݭn_Z_fyU$xHw\qgypěoKzWq4͔ͯv>3e6b k̰7{ GL(N0ܕN1X|"$c}(NgsB"aŹDHBᅔE?pjRi+: [t,.l߂XVKh!-Hʐ$ t96]m0-2)AUC'+t*@r+) DasBXl# $hwi3@tYDYdt5q]zڔ[;-ϱ3Y`᪙y^e*U5-5i!h]HH۞U!h| 2l)P'cWŌqEt=}"NL Op%ϸAE7p,DO䂎|09^>8Ϲw^lKaTOgdL)0TtP_zNu)te|s]|M+{gյg_ڿ16h͔6=#w^oSX_,4B472E^WH3:}Og}N;7fOޝG^K tƪP!,&ػ@f0 Hv|MOo݆$_`v'_W@ a G~ dg / G 8 vj'=^'w`iN@q&~g<\gw hx~ (SXa-!$h$7/ rq҂`M1aX)Ha`X=aR(Rjȃj'w hi@x~8e fNVyY` s%M uNL}W08{ P|NtUqi u,vP }7jGtM׈xWvvS>uw< Y>9oyMi\葔r! ,dH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8syϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm0h!En-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯?ǯOІ@$آIh-l/1} AHHa@ Р@HPF K [ $,B0X(Zآ <(@Bl1D(&P~$"nH%qQ%GD P|Hnjx̣> IBL"4?MI?$! @8I>PрD  m|2 +Kٿ#J@B^hH @$[IpЃFC0qa6pirrH.%@[pP h)甄?pC܂L@Oi)%&'H_3+m1[HG o 'oXN2\F7юz HGJҒ(MJWҖ0LgJӚ82ص@ PMD9tHMRԦ:PTJժZXͪVծ{d2ʾ} L6jI|N.6Ѷh&&Q@Qa )NRa] CCj>9Uz,BZ ˂-G`7=O!.Xg!.][KVo]V+KVIHl˗zQlkc:͸΍tK_~u.O؅6nT@OvFCuk!@Q]@ r(< h ny@,>ϞtsG\0%;MD#oKЄ~~o Įم&z ;ng:y ʑ %y)ţ8'K @.xUJ+(78emY.,?Ha r( Gϯ.p ˏ4_(9nUh-H,HPyf5xM@e!׮ W-uV*a:*FQ'td3 "ZY@"O M@>]p aysd;_.-"p2;F1)(C5.(S+p~'nt/G\~D&*}&LNu0gN3Dw(__0! ,MH*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ZӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8 @-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.촟s\ߎַDF І@nD|yPP $NGHHb1}?'-O/@mDBt$ =H/P@7;H܀~+"цMy!_ߙz nhwYR0 gH8̡w@ M!ė0!`((m0quI|8mPX̢.z` H2hL6pH:x̣> IBL"F:&t) q.&7NF'GIR L*WV򕰌ˎ(Z򖞋"^ 0Eai2dpl4^".z.)lBʊV,YxNmd'9u@pެx{pg;cC}c =7r- Ovʓ :K@^>FDx@ν|-FOY$7ol8iƹ7[ddiR3jW?~z [o "v{ :Ƭz :I U լ5.ZF֧v0Q=3؄YcZg.o8Xj ,A>[Xob-,ewU>=?A[Zvd.4A6SmL5h$j-)t5 rPo wSPT!fe;oGa/5%_! ,/IH*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7GWw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n;@ܖD%@DDHrH҆H8>*$L^(@n`;U.&O!9ʋK.@$'T .2%Q$ϫalKۂ:EȟDA%Dr袷y:kHL:'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2hL6p#aĎ =t198ArD~qdp?zl d Ib td* C` (`| _a0$IL3~2:~C$.gM] /*yLl&S  @ \Nӓ/%6O>9O$dd5\s ?&rD9Odc(iKy"SYc(l FLP9[Z ߰$PԥFDJ=&@Hlm2dzo42:KH/p 0!w\3 Ns w7*dO d aƌvUE_f#ãaYVX4-bQ&Nd2XFКֱ-Y^%^6d=YHljQTFmmoZغV:u!~6fǕe+Z^4  ! ,~#H*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,$lRP<7Q@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.sp8I IDFl%PBآmͻ7{nDRzێ| H 1w#FyoPzKؾ߆$}J<$- HB@Ix~` /g;^=$ GH(L W0 gH8̡wC@ ćH"HL&:PH*ZX̢.z` H2hL6pH:x̣> IBL"F:򑐌$'IJZ̤&P ܤ([Džo47Ĺ` +G9"Y2Zۍn˷dIL`F1Lc,! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^Hb!հc˞M۸s/% N8Ɠ+_Ց6̣Knk]bË|ӫ_Ͼ˟OϿ'hoYg 6F(Vhfv ($h(,?egS 0h8h:@) BiGL6PFRViXfZv`)bih\dpIۂrixH'\{矀W#j(O(z}$ɢdOFjc+Uޥ6Fc XZڦꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫f+^/VB,l$G eKkVI glKјTk,$l(Qk0,4'X8<@-DmH'L7PG-TWmXg\w`-dm٢k1M2p]-w Hd{zS\.IV:~~QCQ>{ ]&6G獭. iN^N밟f/ q^%޻tanAŸd]v#/Vg#5Oi/o>ޟ5_t֤\:'H Z̠7z GH(L W0 gH8̡w_D")D<&:PH$X"v.zl H2ьCF B&#$ґCỉh[qCy $A bC:rls|$W$0G&K\q `"CIRr>)V򕰌M&@*i5+ӥ~)sHfRJuq4dHd1&dE̵D2H3ղ=r37qs!TH>O̬e@ŠAhJІ:D'JъZͨF7юz HGJҒF s>uRMӤ0UUkVU ~cӞ,O>F=~RRP)>JUfAo'THTծGٗW&)NԊֶY \ bVT弫^ fɁu ` H+b;2d'KZ`q,f7k0r hGKҚMjWֺlgKfs;^4}pKM:ЍtKZͮvz$Q|] a 2TH$l! $սy"$ ~IؑKkF| ƋuF/[TPxvI| ['$*f@hE1VˈK@80q\ bFb [KJ`6  2^d-{˳j/L2hN6p96PC?дrA^!zFH}.Ɣ~JR,: ^tviz!.+:!֞P5} nEK<!Ѷ[:HcmO+ٜ96llzζ`sM 7UvHMzG5ox{EP-G/p w#.q\&H)"쬗[9krV49cD5L[sE^*-9:ark|Pʩ=HW@B!Ò߾)VG_o)r} a䋅 =A^0ٿogda/l ~H`EB|W)d`~B''DWև-G  ~.7@($P0Gz  G1*(w@M{7 (2(7.494؂SW؂ʑ7}9()Tw@O^tg17'/_po F؅"h_V!?kvH!M۵NA)&x4g]!wja~Q'\8L8UHiHGAxKd9؁ ԉ !g7 8T#5c-1)HYx|.Q X=}!aNJ?p(j30{3~H`&A"3 W 5|dJ(#G3}W.3a:u(Ѩtzh_|d(i#;7(ip19lюP(YJN!xJ1@Yy@3WHc)3` 2|/Aާs$"h"@D -V xi C#H1YY4)*H ;))a a:njN!&jiz0x!2'! ݗ,9}3J:5I)CԔ"5ʡ0G6&: !T!75` ` #j7pO!ji ז/o 4FC9I9uAhoڞFZ#0r _ g󄖉 Q0G 9:4 jXq H*0X# ScqٚP))8#IR z `UcQx'G!Z8hʟ@#*"ᯂ(=R:A )f0@+dBT#w Kjɘ&#J٬jF{@39,ĩP4(rhAѴѭѫ{u@H"!6^T3 :2A;\Ki!GK5r9 ,fkxԚmKɞ|Tjhz;ΩCxGo?[1N{i],jx#ȅkZE;o  ! uq+9XR)q1qhK {z AQ8Hu8~³s[,jglq4> ǙKF+BTw!B%4),huV|aT&0S31hZAw!j(~Agʴ3 Q`!lKֽcŞ0˫n'l<"đ_DCaGzqzy уzȊȏCgrON!^I6l*.d|$ –l +{(_ 4 (k1AL4ml$BD-gĦ&< a~0[ Q,lw&.<=(Ϋ\܇5QЭYm~W ]&vG1n .o-'r 9謳ι`Wzkn~%y`)/ȣzw8W'/Wo_es/Α觯'~eo_ HL:'H Z̠7z GH(L W0LH _HN~6̡we%> H".FL2%:P"'*ZXtbE¢`!HFnhl!4pH:x̣>zd9cZB)"qrr HzZ&g#39~҄ME,R#ɒƴ1v$,1Yڒh@L*yP򖾤֟F ^ĪRbf|D4=iZ3ռ63Mtsqd6IrL:v~ @JЂMBІ:D'zqRϲMюz HGJҒ(MJWҖ0LgJSh8]MsӞ@ PJԢHMRԦ:PTJ沪IV=ѭz` XJֲBKfMZֶp\J׺xͫ^AX$a{nHIMAvㆴ}  +7Bm@kdZ%P$\uoeH鳨e˘VK-;-SPv-\-k߲qRvmid k-. Dص $a Ib6m)uוAa/bڷ=཯~D8H 2 H`40% [ΰ7{T~!(NWLOdq1K6αwc,?|{ķDNrbd$LnL*oVβ.{˦-"f0hN~p6Y3X`Rfy"w~7]>{ggֲRܢ7-ݎpF x!h7@Y Csi'523z wvf#`&9ѭfq -G 0Ƀ~!ڰx%H[4 ސA_ڷQsHbYez` G ,'H!gw$R3p^=݀9Mn!HusGG&vy ~v{gWto&w0C' guzhVH ߰hpHܧf$ub(`($`nfN'QWtA|nu2Ђ cyNX%}v 24`OXv=32`Kn~ C pnp`phY:n0t%xphnmN]rh'fus1@wsx`X +OBG#ްTI's1U%0Ib]Af:Ќ4IjZ̦6nz8!qL:v~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJsw0Lg68ͩNwӞt> PWNuRԦ:7"@ ժ,'VedXU$b_?+`x^ .^  fMu߸ 8+6 @ /`X6V @8?aفL XkN]Hbm5 6AMSx-B+^%Bمbm þ!],tP]07!mi5;]!3,R.`ZBnC^ )XSMCJl kv5]/0^S /Bm& ,:SpyBv "&Amx+fu ]&pHt@Ko0I@-IKprgO$PJlHFs @pFGyXjn$/P/]5w[! P<db!ǜс dHQPU |:N-}-y` XJֲhMZ9յ@@$¨> _:ǧ9_S%#Nİu* bD_Jv&Bd!*&UM?1D?p4M@(ba7?pb2Ɣ,H,@A6NY2v)!!jCh%%Gd6PEF ӑ3Kb `qroP 򦓇 ?9?F, "[}&}-U6XP f/Jc#[aY⯼$0?2mB|_ܠ\`T0\}IR^L&!OKap) hC nC8 sg ZjfddUoͮF(+n5f)c %n0^)Wvݽ;j!( 7DZܨDy6nIjA73>@6>ڍA7k\{I|c81h# bnz4!c #f):7y" a;3mlAl~@Te<3 !)o;43Ǐ57 ARbj`/}/[ (J}S . >dA-* [@ /? 3p&Zo76|At09}0$.t1) Dyg {Ň@O6fxv Szrw~?:n;! O^#XHB4Ka?Ȇ{柯C!k'{QwS{G'B> ikwx>!D=ma7Lw?(TByW7xp' ` 0b%s 4pW'+AzmwW@rmǁJhqWiRfMG'@DWԂ/XG{R(/ ߐ`'CW{qtSI]|ÁTcUwuXqq?$UBsH}C) Gxdž #}?-cgdd@~n7x$%vqeM iusC~kxoKJ^eg?pIPv VW|2Ԙq\@QWL gx}(s{@} tMw` iًَf"HS$K9Pxlo 0$hREUh&}_ X7(SKhP [jJ1q$u hw3pSqR=iwwx}eQ ă4YU_@gs{,F@#'rUrur3YvM+%fNo#hSD,J2qTvRDrSR֊oI6Hp:DA=niWtYqOԋd?av 2L:3nY)t qsZsO3XosApwqn%&v0GAYmFmyq! ,bH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dIBhSgv-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯 /Hq/+ԶІhC ?%LBla  AX/$  YP ! @ Ƞ@HPF l  [ $,B0Xa(Zآ :8  $Ȣ@"?" I$ch6ύpK9x̣> IBaE*dp#2IJr?ܰA1p?$HF`[%I[ ! X-Hm8 Hb5 `3e$p @(cx&"roID/L*L-4yZ#ۨ/g $*IL50Y Lu8(H$-@bF(hAm@ 0)e36l{Db[l8COuz @ BH!I@4 &"y)Grӣ:PTJժZXͪVծz` XJֲh<Ґ"i-IM\0G ` M Yok&bnj)cAMa33K2ePSN(H0WBQ M?B0h d8o "ƔtAGZS#͢)YHnGPҊ̳N'O[1nwh&KpCMFg $hy=12lN[pWH{ʁ)-CjڶӚ,҄(@0bږ-\$0݃$Jx.H"ۡhbkRŋ$ޔ8-A~r]TAyb!ب49X#mHi'L$ K.Wf5`T֮I2Scyi~,_GAa>X5džHNE]PۍDI=`,Wr* ً)<XP$@V7,(k_Er0jn *S~6!WNFbQxCN]׊u!^j~5rHgp\|IM'N[ϸ7{A"FU#}m!֨!%m)sHg$lWP%`.[4AʷEP ] ݕ>xh3X@<>T 9޵Cf2N*~ ;IT Vײ q%-],܅@7]Z07!%d[.Kԥ[AeSN4i#P&lcm11xalٓ0X N*PIfyR(J(P QW)p RQa|h/"OVPt\P" *Pͨ'QVRB` #tg51Bы߰ QQN .s%șbՔ %!5q rPAN. [,ͩIx rvp (s9UPy (PM &R i*cT&O߰ W0302P3`$g7\+2m%Ow 0/Rp ie,J}w zP$"Oww~p1pL:5 l8#eG)k%Q(Q1:O^ҟ:5Z0&W Z#fc7YSQ4`0,Q?+uԐrK 9 :.B3Q*U<&QpiLj3 qP6h[5j~ktEjL:&"` pZ/Т300:4:l)O~I$DIUO?TMůzdM.)%浰 1)0*JwMI<(odtϺEҪq,Q9%;j 3002N@WS[ƧFԇXm*=*x_eeJ*{CU]ǺS +wxHxUJ%FR\ /zAI{-IE8u6OL 8Bt)1bE%z^ x)&/F{{{\Ơg}6^@m:&r*YPxqUdQhP+k ڸ(Yг (QdzS}%{ͥhnkkK0A51iv@.*+-(QѨPJS+$5kmF{!S S`u AIb]Ыۥ,@նU%iœsNʵ̋a!{# D%QtJ*ʋHSuB2| SÀw^L2-Ĺ5(&:PPvK xZ\ck:r Wp303Ytc5ox9RIc'ZP!Hc}P LSI{<aYn'Y씌r,WM@LS5EbT(L'}{fi#D%Q[$*'j&QO4RNPX\IuV E6o4WV(Q<dVVJJTMI )ƒQ)upP9PLK$)NuY΄P7S?+q-/+(  M <- s4j %5 h +Y!(i\+~Y-c<1.ao!Cn8p ;A?Y?Ou;?Ǟs ` pn - >a|Foqt..@pt sz)G*A ڙ-: G!$sahmw$}:,GН6s (`r -=8Gg=0= $Ԃ-`pQrd-3|XzćF><! ,fH*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8syϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm0h!En-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯?ǯ TBIDD%J!LBJ@P [ T[xAB"@'B@I k`.Cp&,ix k MX F +THa @}NڠĉK8+hL6pH:x̣>CQB {ڈb$a! @H H@HmCt- " B$-HO ?<lIAZڢ$ABR$?W$PF lQ[$DiRӖd2SKm02D7H "4]2ĩ>AC:Xw @1WKhrA-UJ.i7QCB, ' I".QDJ-?nAL[t@lQS?:> PJԢHMRԦ:PTJժZXͪVU` XJar|b,ZֶU=_}\vӹxͫ^?wV>7J~H InD |4A `.\-PJY:P@] ~6Pkw]h#i hBq]4 @f ൸խ&W|iSnƍ#W[ςV}|3E @\(  \ĞػE @0w۷qPA].)@vU۾Z‚\ Ŀ/vAHIX|UL %H$ZĞɮA] .@pҌ ʼnuAcpWK T!J|[(kLb7q]2gnd6CYSk^̿9D 1g,?#!PpBXb5a @*P_ }F?3*n3WPngKoP~-,1@8iú/-W$9w[0rhwߨ3Ak{.wҗ'̅@"8W!)q_ J+D!i `Z (IK`ayʔG-M[ZМBMpp",{]'BlN x>M>Xk*C&p%lO3߄+o&~ծ =˝irHCԗ,'@;_J>$oW.or4M` w@N` ` w w~ D~R{65TtgtM ;OLpf H*\trPJ%` |@ 5gs!i+]%|$H քM'}YC~]%q}5q73X[u'hv4H9PII t- u7X&3xWi$`Dw:5wa hH &jnH`8:l|Cp MPh LWWV }11@t\lX"A!@ dkw_P6|`egR& DQZe8Vn-8x$DG)j%XnЅ"qSpHgwu  M)5~5ghjVh* @ 9vB$ $ Mzevlj8t DݕjDRg?bwqGpt+Vh5N(5I$$pd tX"'g)o+gs"P@3]tX͇ qvfև"1[3rЗ!h (p 54V8xiWq4)z8!W0܂{[M pYXcKyђp =oPXJyQ A^=6qI |ߠ ?IE&BbK(GZבR o @) Kw]- wP\_M_ͥ 8|r 4iy|L]7[B\e4$2bBjs'btHiߠAr9cK*ZVp f  ɘPK(Zy/ Jq\"ZX! *ZZ6pdס u +Fb/_2,:-&i:\D`w(ãbj Z \z<61\\դ .zz2aڢ1ZUN`_$9^eZ ƕoQ3ʪ9Xf}7:7a^լA%jL2[P=&ZT_U.5:M^y UJ3~*EJŊ?299SN53GM5[/fe O5L2OKTr'*St2qw1|'g L0ho!q*2 QR Y#S:[jv0#~ %Ft7K27Lp[0R˶r@ȵD1sH0MѴ$#g-fi|SMv0놑K?)\,1gJ(ؙ_3du7UKN1 J'Qgʻ1DQ` `  ᵽ1pSA$سy 0b$|i0 t [U1*0@@ HO' Rw> J׺Cu4OIh;>GBrsV\EyS@J>_ͨV&$l=7,yvp QIL=3 @+)\=iAA\>UmjXH :Eї>_?Ws (s Vt) Q$~=7vAf4x # YiERDj*MCG ǜ[j>5XzY> M߰ ЯqÞ` vp l<=*ǯs! ,cH*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPj$!իXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmh70Ul-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/~I@B HwI-I~EP _"! # H |[650 #$To#A$1B `($!S`  dmp' hᏁ$w$AE 6pH:x̣> ߨ crHD:]|$'IJZ̤&7Nz (GIRL*WV򕰌,lcM6F?x)p1Y`쫥$"hIZ&ApjlY-oLFBF0?FÁ`S h6CegBnPHn5'$7LT ޔA6@8g$CRRx!pR#yyz­ȢG}2bxz2t!AuKkh PlI3 hbR vL|?š_B(7oURš6J[u\Q!,56:E`.,'HͱEYR.2ZTmnjITf d-(jȼ*5nf`6x*/xH"=Y-ϹҖ*3!FsO2&@orބ?ْ%! +lbQ%gU]FBNX \ӫ9$u``PۂrW2 KK4%,$ oS,:X${$Xn/ִg5[&~./m!c {jĜaa_WH5_"t[Bl9#q dм*Yz+;i9FQk7{d+b|li׿_t&Y_PN b3h3 3q M`&8PQw+! (X9iC"/8B'#8I 8*x/ 431H?B7؃>H/G9 {GJK؄NPx#'G%2VZ-F^`b8dXfxhjl؆npF80ix8s8>wxGؗ}q|{87Th(7=! ,mH*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-@ƅ hp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯>FH$@B %H$l}s 6DB;Ă_$!Ap _AB(AKB8A$p @0x%$m@s$B 38! m$PȐ}`"+ (HFhL6pH:x̣(H !> cIrbAHB!`mآ-lEJ !)m@Q|"+gH@q%AI7aˡ' XC?hL<+YIR{,A/ Kd2Д܆,+=U 'AA7QAp̧>~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ2t$gtA]ND B(0 "'8r\08@*pTUTGx/ Tp>q wK*5B. Y7 ) @HIH >`*[:Lghb6>4ANPMݧUGjMp1JG\ Vys0k#9ҏi*"rMPYNMG5멶Ax S)X)Du I"BȚ4រ:y$! @]ж  ,١4I4(_nьfLz4'İWܧtxGpw߾Xh|%h7`pAQ#,m0qBiC)@ԽE R))p(ID`2AjAu7v `IBmx61C TEp ?i_?Iw!- ,FaA8@¥3M8 rp%rʉ,j*W1Y&a̡?gB J2aMYtL3j) #)AoL\1ĐgCg#!6zX {%D ʬV+~HQ[E.x;B Iv8~`ERnEO% 2 j4A)QwG n+am2ԼFɣ 9G,y͒+jv5*{2uEzKXKF=C%` g vym8Ɛ-n_:3D/a"{$t R92n Yj!!a?AO$^2^|^H >$@>DKߠ %` ũ|2Yrع9xeܬ9#|@@ pe #룞y+ϥ hޣh@ q7疠s Z8YDlSj0>晻pIN#M.b{=ǧV`uWWkMb8>)0jIp `aZa^ʡztH8ڤz8<! ,TH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`, b\meNp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯 ?B$vH/$@ pCA>$ $l?$$An-$ nEV 7$H(A 9 Io "!@Ⱦ $ LEMtC 0|_ l1Bq CAA+X3"gFXpH:x̣> IB?F:򑐌$'IJZ̤&7Nz (GIRL*WV-؆AdylCІ@di-f%Х@f نH4qȻE(&"Qtbri m 6l.r x0EmP@^ A$ FAwN/ .(b<؏d=k)wR@ w$`'RI3y)O/ @ J{) t=dyMuBP<?XOGB2(Mv* `&Vj?&;*v"hnfT ' m5\b%Ty>_\6s uK]zT9}yK:U |}lgKͭnw pK7w.mD\Dk]ڃrܨ6uהj #4uà RA-*Kld' <IT7(4Y}0<`aFɻVkQ|M)_2j &fWSc % ZD~-FF*OwU' 8_[]+*²V$j.iD f~L6V /=E?K"8!M9O g\L 2 8haXYd[ ,+*/E+1!k مStpi2 !abuc!hVFA ơ Czkޟ+bM"[Mj~7CO;.l!A؅Cě{)\JO\A 2( }/"\\r|C/B `V^\+ o!3/R̊V;y%A&0S}31(|""26lF\; v<`WwV `sΓN2pB hr6 foʵ HL:'H Z̠7z GH(L W0NϣH _HΆ8̡w@ 3!)"&:z{H*NtV!.^ (2r06pH:x̣eF(,ܣ IȔ:BTȈpScWhNI[@ɤd-i&Q") eci3ua%,g©k1-wPz+ch|%/-Y}eCf%4AgR3_n ɛ 8IrL:v~ @JЂMBІ:tZ|De͝s8%rn 韜QU IWҖt)xLBNwӞ@ PJԢ&FMRԦ:PTJժZXͪVծz` +5K*V<)Zֶp\_ҹxͫ^׾ `KXyIMlX Hp1F\ yfIP84y, F @bAkrV e%X+jI &TveNІַoM`ځ!i.r6 A%"\%8H/Ȼ=S!zG+A,zDR漤_ vC-OP`-o'H|ϐ.g]nѩ"JOdR݂8!1 /}5 WzE0'.MP Z . G%*'&y $ t|'J Sx;8) HC,x(}}Q~Qdiw႞k77~W xQ(A1p"O=Fc2vejSpNHLAIwmʑeju.?He~=hyHR c(H!S / zge2Q 7c=(qpVRBY/.PF,gPGQv# "Hz }ΒhVpNJ+qE-ڨ3hWXyV̘,x35gNj!6wxO(x} 5zN"Xm˘ x,1!9XR{."I.Y`- 2sX ).\.H2 ^!/)I&xW X Q2Yx)riI ?Gu|\v,(YMv}\!)(YNRsf|Y1` U`7F`l6[С%  ^~iw9IWT)Im.O KvfV8|.2NGfbY6Yn K2qb9C QH j)m%2:Y%y4hn{;CҔ LeB1e-I# ө Yy)ɝ q( &q#+S:);zy=v4QB})6.k3w@Yy!g֨HZ#/pLSeavdi#fF]SYvz#SEh Z(] ζ w ( uC2YV$sRE)` I@rP]%mSj% ([|YU# a̶J%}*ZGqW7*]VCblVh( 'j[&b}JL*4:Z $ؚY@z;"f%xvPl¢p '$j %ry;zazQsPj+Ԕ5k2':ںm*A# yzКGb+y$c N #q01xX|hk:X+d;)b#=;3q{!JPg cx;% 6*C{ӶqQd˸Eɰ0FX ui}(+Vˡ9gH#@ 2| )&(F_¹$ӻ}5rR@džSk|: {$BgU#ЩxQJ-B{)9]KHXg<aKesI+yBaRE=hZҚ6OiK(KIe,+r!<TK1=-M}VJ* FtAn~Dmm~qV!+~9芾>^閮)z~难>^jIzjP! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSװc˞M۸ͻ N<&ȓ+jg:@سKlOӫ_Ͼ˟Ou7(@ 6F(Vhfv ($h(,H.(4h8S L+cIK0!$V~ @JЂMBІ:'8#JfM&ĬF7юz HGJҒ(MJWҖ0LgË8ͩNwӞ@ PJԢHMRԦ:%T7v&VͪVծz`]2JֲhMZֶp\J׺x5V׾:*~ˡP 1%bb:d'KZͬf7z hGKҚR[[lgKim];薎|+EoDz5N jMz oܤ+Mz׻EĽD+ͯ~CO X0=I'L [ΰ7\q GL>Bb(y1,qwn>O>".@ ܑW41,L*[.{`L2hN6A}L:{1g5YZ 8V.y+3\Do)mI[Xi uNDmZ.w:&>}jzhV[-bNCְεw WO\̾frҺٷ"2mT &Hmt~JMr$Y6k}g3]u7q7V}87VT 8YKsM@]^1"gOWKur sȺ8'̡<,HУFOmU;PԧN[XϺַ`NhOe]r"owR@! ,cAH*\p #JHŋ3jȱǏ CIɓ(S\d˗0cʜI͛8syϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)t9 v矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+ľB&6k$F+mpLhfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l><@-DmH'L7PG-TWmXg\w`-dmhlp-tmxށ+|N΂n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 c8̡w@ ÇH"HL&:PH*ZU!.z` H2hL6pH:x̣> k҆AL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2˨ a0 ! ,\ZH*\!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʔ&PJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7-=TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/oM HL:'H Z̠+[b i1TAa I/h M anD\$B[<F "  P# 0 $G$ B&h ) l$Q X6 $DBeq"l ; DH ;F@Uְւ$ h'6Ė#@H!$\#ˁ *HX6,E#<2FH87 dITfR  pIS"bG$@q4!/#-BҐIha&H$0#x:ܦ i^M:Іrlzhb&M."L2 t# ̤)!u5 yUv.d[w:~4!AHHnQ%`/COLJFڤwTˈNhRՎO橫|;rхo!E\jݼU\ tgHWt>eQ% 98l GerBN5Cf )عZOӼUKHQf5Tz&^Od;2peAZÄ#M)H7JBe $HP-$@ @$` Dp m9ܢ Cn" " DT 7@آ+?AA nI`6nm 8[hA`a$HFшC1L%QPybF ̣)q |5 IЀD"-x?r 8e2f:Ќ4IjZ̦6nz 8IrL:v(z^$>݉2w'gI +@|#BC\ T@Qs(NH q`*)J Q.8iH]$HUP+E**r#P_LTP+Sx&^P"# Moh$nHBLx8[/f03pBggqO3"Z&Hz\bGw0 &>[¬6$L!G`1 ;~2xAh> =. H|\$-&.QwD25@t|&'"K&9a9m `!Z奫_ f$w=89QKVUDl8?B;$ 0O"qL',2LԁQx=W[_@Hh:.d a Fk1lqY&U /NPR2Q1ã"̊4Sl9XoϚںڭVg;DSڮ/ZeVӤjP#Q3:}Uv! ,YH*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-@ h&En-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯?/)D$F ~h"1$xC$lIPے[x!AІH$0,ԠJ $DB lB %84phL0m`B$D ؇@ nhC7F cH:ZЎx̣C= IBL"F:򑐌$'IIc mNz (GIRL*WV򕰌,gIZ̥.wK6HLml㒌B)dF/ @CBG?~cpfD@t7D|V R D~Dtgf@C-&f SPtISiy?S3әWvˣ]v6MJڨmu<\{"؉]}3kRJR( rԵł\in{ l=m]ԟz EoӜ(ĮBޕp Sea6ŕMv:2NaY S mTq: d䇶3l+[쑖@)8m3^mS̢J~[CSaj_t},~+"Dإf.&vx?$f*ѯA1& Vz2g*PO s>.Sv$jWVb> p R` p'] p11w )8$tr1>x  8SR@*(/0*3Ѐ@BVQH20RgL @ a/ O 20ۀ;BGgLrLw>x/p$f&F5!s=`VB0s $u|(=?3 C(&_bTlD `Ahwߠz|D N`uV=N v%MPu(VRW0ChևMX $'n@$h*dN0(qЇLŠ pZ8WppRqbS⏃ҋAG ِ89(*ef281ָÈGG#Y9(*,ْ.삈094Y6y8":<ٓ>@B9DYF(tJ0h;! ,kH*\ȰÇ#Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͓nɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l͛08\D>-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n !@m $!P!%mH[O oz GH(L W0I@lć6ġiD1l@>$?h?آI$man[cZ naA qڈ 0no6?JŃoy(N% DlluD9NQH"Q%a:| Dڰ"A(ޢ9J5 $ $U2I+L'HA R! BHQ*Yl {-F  h Fm6k4%sQ޴~Oۓ6)Ç1Tc4ЊZͨF7юz HGJҒ(MJWҖ0LgJӚ#tN%&[@@7~NhA|..N!Q&G_uRT9 pYUP8a"ױm >W[pV2xA.)(VM-^^*Y6o۪ Ϛֵ u t)7\~5LmX[@Xɡ hpشiMkcք`}* @v-BۀiƦV (ZEKڼ +-'p@;4aت|˄(.X`ؖiMį#Ah&PWlb;^}A ۾ʶp%,ORYzM$\ dLM.EL HȋA V=Z*Bc3ĵagPx AFE` R$e'4d&Q T6L[2B,='"@rBfMw+f!ٯ& MZ$ 0@.qԣZMz$YXH*u]j0.Lw]Z(da奶$쵁[;~[%; ڢ Mx!V0 gH8̡w@ HhHL&:PH*ZX̢.z` H2fL6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz2A(GIRL*WV26#6'Wm.M] 0IbL2f:Ќ4IjZ̦6nz 8IrL:v<8˫! ,H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg4ZwZpdmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogwz$IT?Q$@$ABL$} GrK-% !='F$s"! W>瑀ID 6H nPH6 %hŇ< A'= C!lA7HH`ІD^BIp[$鏄Xd.z` H2hL6pH:x̣> IBL"F:򑐌$'I@̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f3I1̦6nz 8Ir&Gݻl! ,H*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmN֮ \}Dbmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw`/~$ @Hh $D\DnD"-{-mH"DGZ|pCJA/~K͏ϋ"EO<BN},H "A$ "a Ok,^׿o+-8?0zH애m(ADzW0.z` H2hL6pH:~ṿ> IBL"F:򑐌$'IJZ򒘬?2Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ26M|{xܦ8IrL:v P! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSNJb!հc˞M۸sO% N8Ɠ+_NՑ6̣KRkbË'|ӫ_Ͼ˟OϿ'hwYg 6F(Vhfv ($h(,⋝4h8<`D(DiH&L6PF)XeGXf!Y`)fYihjpfti6|駉s) jhw+h2.~y 餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfnn-WB+$GeK̖V оKɘT G,QeVw p$l(,0,4l8<@-DmH'L7PG-TՖq\w}bu mIru%yo&s&rw|V GhA 6t5?vS4t8Jt{uywVkԚ`'x{lWP.@n~ko)/(;uR'/Wo_ẽo觯> foʵ HL:'H Z̠7z GH(L W0NϣH _HΆ8̡w@ 3!)"&:z{H*NtV!.^ (2r06pH:x̣eF(,ܣ IȔ:BTȈpScWhNI[@ɤd-i&Q") eci3ua%,g©k1-wPz+ch|%/-Y}eCf%4AgR3_n ɛ 8IrL:v~ @JЂMBІ:(I)ъ >1E7юz HGJҒ(MJWҖ0LgJ!R8ͩNwӞ@ PJԢHMRԦ:Pe!F GR3HVծz` 4*ֲhMZֶp\/ x+J0U~$#ڕ$(A@X d E䮍Kb+IJ}K${$؁ Zف m(fU@”,m$Z!_[85M"ܶT"nAf[ݵຖH &پv)@%p`s% iq/wR5d/ 10;'L [ΰ! g-S%@⏃C~&ߥ6I-*ⰬWH=!ȏ!{'ǒU<Cfc\|.Ӆ\v<ہ2itxp^ \7tγπMBbNE}F;ѐn#MJw9܆b0o͖i ϝ)QR Hjhy Yu7ݕ0%VAdb>CV]f7)"}iJ1ZۇJ.9gg.Xwf7`QCz &I |="ʁD(@<3dxP>KH6{|(E(oc!Q $" "n(ݚt r+yrjǰ2l{GyB.+drV~'ܣq2|?bov*A>A#HI2|DSiRmCpMW x3~L\@nó Y{eX!WC[X;A"^+q~!9D[vxv" ܔ#~'`竌&| a>BB+ƣ[ y_@q_Ԓ7W1E} wQ9"eX,{0}X؁:"؁%X|"m a~Hg,#8427(35h2:E(,#AH2FW&RvUdX㷃Ba AyBN6Vb*hrL&nP(VƅRBXX2`H^bU&|3he(pHm1dW6YB(u(humR*6{(e+vrIކ#nb ȘxSj87uxYHu qPRx r(r @LvQ#y7i`lx!qHe,wG1{wp fWS` 3 i3pr0dat8W#1dnc7s&hR W8ܧdH$p\/&y8 S0 SSn{\PɎR S/ )BؓC7q|loGA}z\BRvR02030PjH 1|fwVRG:ȂG72 ȕJr g )gYh{ ax!Wpv\R%yi /qpSPə}q2ۘxÃ!o77$9T)i9gSX1h̷q !9rH 69;)A{'K8R|i)sl'WkVEIɟ q H@Jrq/^k)Y"єisEabRvJ:IY(gZ z&ә шzb'$Nўr wv7'A3Bopͪ$Js  @s2e&<+T88fp3Aztt]ljH "Kh4Yڀ|I \֥7泏;av5{%f%B."Z;92xJ!4y0FkǪ0;z;pwIDw2v&90x[:xh1/G]aZW|zB\LbP3k} sb@q Qvz CEv'_;Kb(tRu1aW8q3^YY Kwc缵*7k{ jwohȿnE3`K;a\ )oKxI SЋS'ѱ2\仔0ad\R5,8L \S\~,Y.t'(Ql-AjdW W8},\^ *e|QSĺ"vyl{\}|z|,~LnəUa q"ZsB1L%GS L+cIK0!$V~ @JЂMBІ:'8#JfM&ĬF7юz HGJҒ(MJWҖ0LgË8ͩNwӞ@ PJԢHMRԦ:%T7v&VͪVծz`]2JֲhMZֶp\J׺x5V׾:*~ˡP 1%bb:d'KZͬf7z hGKҚR[[lgKԢ?rK=׎eoo.r:8Kt&R9Qu r(5wKq.zKľ+ΎrL3N|_$E0'L [XJ7{L0GYˑ(NW0gLƚU @>OBHQqH'[${HL2QfN6pL:xγ> CMB:aN2/< Ѳ i:Sti3E[yGMj@ҥNWm'N/~q:g@n^ZƺMb:* (we̳}j6A=n ێ.A=r @n -PP/ oK$ϖʿ=|S8A7Hnu'^^87~r רCN;#s n)gN_ 6yVQ9=>;DO:5+vR~zl*[XϺַ{`NfOpko;! ,X kH*\Ȱ#JHŋ3jȱǏ CIɓ(S\1 ˗0cʜI͛8ss$@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tixj@|g|{)蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k~6~$IV%Vmv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8΃A@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀@n8>7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8Fq@ H"HL&:PH*ZX̢.z` H2hL6pH:x̣> IBl"F:򑐌$'IJZ̤&7Nz (GIJ!ꢄ3 RRn2heg0` -cIR -_0[lȌeAA\ڒewId΀\->Ԡ5c)] 4o;|%.g )R3/cg繇]|=gPt*TthD:Aw& %!<](MJWҖ0LgJӚ8ͩN@ PJԢHMRڶmЩ"A8SaTՠV $թzlm6AU[]]V x \V5\j QHB-Z `jP@"$J 1C;iIEd[̌0V h4֤-f$(ڴ<` etNr0 ! ,XAH*\Ȑ!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ Jі$*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw%$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n|/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ H"1zHZD41hHȳ):Q@[H*n?xEdbζ4fgt8F11Di|l#gfBҐn!C*Z! CJg$HE ͍}"{2x<*r,Y̥.w^P*`mjIڸza⌑@B1 4mlMp"A "8)Zm,f:w&Iq'ϊybOv `9g6Y} LlI[h8 5(iKDɇ"hΒ)Ojl8@NeLhC&Ҍ m0J@! ,XH*\Ȑ!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+kj B ,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z0>GH(L W0 gHCc`7n( [؂_9!!JBB?n0_۰6'Tt."$Tl-Yn0]U4EuyQDl#UFaLcXE@ю~ȮmhCHHI6D@$XB6Ф$ŨA #iGP2x,HPnR+k ; -q)/G3 L$I$AڐJDmPӔHa ` o˘>-"~43KxS= ! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫nY$+iB D,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀N?n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(LauB\/W c-3 e!\ $s-H?lQ@tD$r"E'":#&q_(2R1C,"ppYxF?BkPEku6ja m4RE$4$(JH5-8RZ"pJފ@@KV ! ,XBH*\Ȱ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜ͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlg~0<D"l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n{0/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX,,z` H2hL6pH:ڱ6jmi,XmhC$!? GB#-Ijd JD>R%)N Kg,6) mHY# [BH- O@Gڸ/IJtQg?J >G#iCÖY-`h[\!!OyҖ޼)8 Ф GhV! ,XAH*\Ȑ!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ Jі$*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw%$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n|/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PHEX>,z` H2h?r4Q H2GQ f 7l)<`< `}c |d'oԑfߐ9D|F w#;I?ѕWY_ DzҖ|dy JR%_v 9pғA'L)hbf XVZR`HO.<62[N:mSd7=)rҬTe>93K& nvi.vH7':) fr?$HJa YSwX+J R?eVG d@ oB9SlhDQ,uhIPTQ5h;mԟ;6$a[xթ`mC:ۨ->Ea 7^:`lmpը\]϶X@N \ūτZMF)U! ,XH*\Ȑ!#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+ B ,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z>2z GH(L.ddq sCubGAd  h ~1xYb N?@#p7JȠj7E9X]"EwX#%Ѯq#o`(m`YBˎ# )si@Ht%F#m Ib4E[BI@ iOÔ@NN %)G\܆6 <L* Hm$$ JmR4QiHZqeurS^<@! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫nY$+iB D,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀N?n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z " \3B[# < /` uBHa ad;xk0?qH^ح> ?Pn2D;l !1[^E%?s@G$|C?! ! C e-4RHP[d$ E(FG Jn1܆-QVr&HVE iLHR(}-C Q@ik$3LgJҜ9eMI)! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS%װc˞M۸ͻ N\&ȓ+HnУKGz|nӳk.Ãn.ӫ_Ͼ˟OϿ(h[Uw 6F(Vhfv ($h(,"O+cK%h8#i @)$D i݂L6PF)m3NiXfkm`)H]ihIkpp)lnix)bn矀*^ j;,Fj6>A*饌UiWvX*_~jiꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫p k[KT `,l' 7-oZz4qgqA/UP$l(l'H Z̠7z GH(L W0 gH8̡w@ آQĈDL%:PH*ZXLE.n` xhLػ5qx9щu_h(p IBL"F:%rX;HZ 2#H%s?'2%* yJ$O҂U lRʒ P2p%0)ai8*עc:"(gZ]ILt3!߼8qӅ<:QTN"R~ @JЂMBІ:D'JъZͨF7юz HGtNҖ0LgJӚ8ͩNwӞ@ PJԢ&=RԦ:PTJժZXͪVծz` X]ұef ֶp\0xͫ^׾ `KMbk:%d lY hGKҚMjWֺlgKͭnZe pK?=r;aiܦH7ˬ.Dzt2d7N {Io HApD`;x+ [1 7P#apGLxN>WQI1g8αw@L"ȹu% @NrUd&RHZyDzd0+:2E'-9p>2L:aT/>πMBЈNx| [H"4whKb6n[<ȶEW-jIihN?>iV׺AYkmXĭyHdƶ]2yimPƮ^6qc6⣙&FVmZaH6KY҆4Wmm} FVf޽"aܛ3Ap5KǸjO7,wz8WWG]j'7粰:miIdZT.ysAXFBt}M Br#K[_r|ao#vߖ]@۝}{NƎ޹l~3?` zw!28WJHytAD_]=^O~wKEn % q jHP6Jw]H @(~, p,& @He pW020VpЈa?(f'v @K R33N`^ȇtRI w082 ((w \xv?hpR 0э/0Pz@+(p XoVŋo @` 1aH)@8&q8(3`?x6U(!N !8bh븉!868F2v r`؏W0ְ I{!{afemz` 6hq?X _mЁwZ VtK)Qa[waa^Xx:~K ]&vG1^T .o-PX&r9Sι`Wzkn ~$y`_)/ȓw8W'/Wo_e]/Oˑ觯 ~eoO HL:'H Z̠7z GH(L WpEH _HF~6̡we%> H"FL2%:Pb'*ZXbE¢`!HFnh\4pH:x̣>.d9ZB%"qrr EȤwZ&cC029~҄ME,R#ɒƴ1v$,-YRh@L*PҔVF ^êRbf|D45iZռ6)ͺtS"Q8IrL:v~ @JЂMBІ:D'qRͲ(Mюz HGJҒ(MJWҖ0LgJӚڴNwӞ@ PJԢHMRԦ:PT˩nFmV Gծz` X,ehMZֶp\J׺xͫ^j*+@@ U%b%d'KZͬf7z hGKҚMjW3*xlgKnw zoq:eyq5:Ѝnq;vǯ]dqsz0.P\kMz|K_. H~%_f;X@.I[0${ س;W0gL8α;+>@Ld7M \ /\8!Qf%r7"_Ls,2h.b6pL:xγ>πF,BЈ>+e xЉnH1Hx͖t]Fg!GMRԨ&#T~5Z,Xնn1o^zvȮ_4/[KGAmj@-nG@-r >H ;J6AA}zۻ%NH[N$͖ʿC{?DGn6'N"< H+EJ 9DW--DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.J T t7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w< H"HL&:PH*ZX̢.z`?q [  DQocDޘhC?;Q{@2fD6Q (HmHd%Imܢcb""Ŵ$h$!? FI2B$mP ebn1 &آ$ &x@)ɯѕaPm$!e$6`rܠ ^BĤͅlrHҒ|mR ?3 6[H 0sNU D$G3J Kde?bj,Ji[3=`*.A)OK*4#&K! 5BMFr>4S=b+eC8 u G;pNӗFi`cӁT Ԧ-HS>eHғ2u&c*2Ӛ|?N@3T@$Q1ym?!Hζ!}<[BۺUyckgG< J4bT tKZͮvz & :h)P}"Wѕo͘ރ RoEKJW)#K#Zq^x&)I$D3 B F%XLu Ƀ<ܥ,- K2hxfoR-rffRl2l-%w޵T0ka x4@"kR~mg޹Ut\^ )fzֺīk?sFRmsL I0lE#Oւyl6HsUr4֊9{M U U\rbm0v6`ގϪxϻOO;񐏼'O[3{_N@J nBtn#o.1<> ͽm/A柳I(jS鋤RZW#l D=b_-]ZO#Z ׇ}~ @SGq*%K/+>$K X߰ {030Rp{ >&K{3h0RЁRR `I$KW~\_|V {02ЁJ؄K؁ {|{bHeAz0SO<` 303x33G8K÷y>lNKMh zE{qiAX>J66؁3Ȉ30iȄk(Nx{*x|zVG}I7xszȄ( WPgj1SLyTW$-563gq 5j/S &HdU)FEhL*XLUpD3-pI؁|z8~d4XHP*L{W[f1`XS2(/ N` ,O&sae xs/GFdH#QpȄ5Ȏؐ!wp~LUFē$铦"Rh.h$572P(R8pˈ0N ېpx^dkgT!xUeLVrM2~ Gr29!P?Feٓ$ٓdGfVYm1zex2p|`3Ly 9OiQ Pɉq~eV#H$*1Pz SyJ .zQf}VeAMa~.eVH>4}3xȈ@D{)֦aqXVōxMdo38 s)KSJ$|9QH}n()@Q3ߠ$y/ x)Pp[5cu3ȧa N F'r0Mh ߐ^H\X݉ 273{hI=`x !REV#V~5~HKKW$ UֶRhfe~55\8p% rF[|^pIHP\+%pz+G鳥v؅T R@G8(Zy>;mHdFZ-%Wj?dIE eI}({(?gaQ{&K6'9@3~p hTI(`5[dLz`v3h ϕcXd?*{m }%(?{Rg?}{%n*b"z[zZ'z2{*~:#! ,H*\ȰÇ# ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPkJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmX 4lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觏~/B @BnHF}_` [دKA?"@ @ $П z$=PhA@XЄa$nqB PG CA.P{ (pC( !/m@ "Ep~ 6 nH:x̣>  A@ ) ̞6X6PLB%ymb? m LJ9D `-` Gڢxe  *eHLB.mDT2m@p*537 Pd4H^HajIL%g<pI9L?B^J⇹ԆHI{JBP?e8JB┄$`CF&(MJWҖ0LgJӚ8ͩNwӞ@ j!ԢHMRԦz*9P )I6hB$I=⬛؄ >8AMP P|s=]W `KMb9|ߐvq LAz hGKҚug4HǾӞɀh,m  XcgÇ"г  j>%  :ЍtWq 7;]om.fG\Nn5+@ M \ :$ nA*(a|%Hz1|S{>8^ R%- .xk]3Ns0@A8_'l m:h&ZcMsCR0)oqkʀl,E\ +KTRY1RFٞI(@|]k[|l^7@'Myހ36K>W~ ǿDϪ܆ׂ=o;nΣޞ[Zgl-ga]^j9An@G5BYЎMj[ζn{Mrܼ5n! Z nMz.-Bjä8E*ςs ؅%\ /찋DNݽT:ou%(~ ܾyo^wخ?:$,Xpq@K_L qIMgj}C*DFpI;G$o|)d ! ,H*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻNȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmٰ΅ l-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯>RO0H$цm(J@7h7O)J$D Hp`[ I@G$!,Hj!@0n@H@AILp}$0\`t`?@nJC ApC g D-0ZP aEЀ#@(:x̣> IBL"F:6"IJZ̤&7Nz (GIRL*WV򕰌,gI㖸AtymhD$ĸ 2"D19f,ĥBh -MC $nD$&9bDI|/aN1"A|)[hgss &C:(O$&*ƒnC~ - "QM '*CT; E$h $٢DA[Re >ϕ &P϶:DydP@fF. 3,Ѭ⤦Z)\gZn,a>b$hi4Uf '5łS+AJЮm@qH@FDKN]k>J۸ŮɔhfUqp?lP[D$`Ę)UF![[Z-Q=Dm8ԴC-H\Ժn)^jw}\rљܥ6zXtvX !@ꐦֆUJN a p }TGzkvst[p돉0n $L2&:ڣIhC.wՃt=jkz3—D@Yu_ާ@ _~ V0B[fx.HMyf٬2ėO\BЈNF;ѐ'MJ+& ` rgYQNIs9$ήvH΅Lri̜sJyL ѥU] &m-9_ 6ٺTqG u}ZjНgP\l'DEyh @jN6g$>n5fՕƛw׋0 vsZK2QX@IQσܜXlY@mkxf&ȑe >\;,d:YChwlASsk>Y-L[P !j=y ZΛ2eՈC /qۚk?g1vrA|ⲳ|KG!eC4k[Ss1Wby >/v[HPa4 NcEM{QPrv61 % xj^i9׀8Xx؁ "8$X&x(*y ,X9.284X6x8<؃>@B8DXFxHJL؄NPJRXVxXZ\؅^`b8dXfxhjl؆np=tXvxxz| Ci ZsHdTc va6>ЈHVB@I\9WUHD[-usQ@ae=q@ukdVFm"dw~ǶCYه(I! ,H*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸]ͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dLBhKg-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯vWT%PI Ih$"$ @٢ A?OP A >0`AĀv1! (B$paA /Y@ ,A$na 7/JІ*ψCu(ċ\ H2hL6pH:Qx?珁h# 81Ԟ? X$ 9,6G"AhC# 'oA0Q$?JLjC-pa)WLrt-щ'GHB\6R $a1gJE&D'WrH1mm`&rxIPs )UvFc Y^Iآ@')v@UN DX @L@F$ ZxH+2ӗ8ͩNwӞ@ PJԢHMRԦ:PuS1 JF<RӄlujX<ե?ƺ=5}$ Wm<.1ZeZMv.]+Ą(=-ζWp{]) yC x"=Zn0L?D\& %ZmV ̫?9T $JлZg$U-]NJsd-H#RѬ奋`ꬷa)_ҥ*` JAvL  /L/BFZl]Px7%4U& P6ڂ- Vl.`E)?pLwȧa@[u HGb ϖ.\;!(@BrY{ײ JF0Ip?,r\pa!ldq&9S4 2L۷TyUVA V29 ZP-?;3K,@@ZPb{ByEJ->&0;GJ0ɿ-bCK'iLYpŒAS vk^`4!f%P0oh[K_=K"?KH.i X:۷.PÌkL,oi _R>HWM|/8҂)}-@ Ɯk\T@ rw:~'DQmֻGl]Jnr[|@G_Ϳ{r\R*=]8C\XyF ]4'z; xy~uxKQ8% (=>:=ImsMF bpՇ^v=l:,mR4//J jts+g6h8SRҥIت#d@ٷf$Owqb+DY! qhN7ZNDI[(fZw}FB+7M]7QSEpb[@p~@u1M$H^X$Hdcqv! :gO@H>(adlCA׆]|UMEaUM5ia F&EhWw,8[m"_^EYragPr &%| jbx?yDDGa+$N|IPTO{TUcptZ &EXO{4Zڷj(}]T%gjM]N`r` V}&} !@Ek@H{f|jmP@twkC8$&X YՈdG[t6DՔh2E@Z?@E%XT1Mq؈v&@k?N[ hN.ȅTGg(V6`(""&v INրhu65v'qb&5Xb A(D8#,)GHE "ƍzfć<r' ]D&Q6щ(GY"u${pr JtMqf MMS!']&}hi#oԓ+qswo.`M0n{CH A'Ę*bBwiTbJ@Zf 2j%AHEHx hsUM{vgÙhm@ M9t.iߖeN㚰) w )SRpÙHНFٜPeU+e4g%fozb @Ugsrqw6Pf pe7eBwZnt.xEyC`s0 )F%bgVG쩙lKg3j.xf{{)yQ )aLP7:x%64b.  *ZX:ƚq R y uY 7sX6\nA" ]5zYbzoIN}hC.1R!B'(p xL7eXƤ N 3Z6() -0Z0JGmJhb y. 6` #EixYmZe)J9J6v㉘%*0$J 6˺b>k\I5s$0z(u* JH*z+S9Zbs}]m,!eB úy+) wUDQz):sj ۱Y6s$@t/_$kq 7e(tڲQ1n*uʱe%an %Xyy5H/??Y` )L  {fYƸoc-*z J;rz{,u62peK`$ $wOɘ#P ߠQ4LVD8\(U={JT( JԜ|836]X J ړh=M7VZr]LZfF`0=W  nSv'B5D3 &̥ vb1 Jjbzc+&QsD&P3"% togکWX3[-* ->Mג.p4kҦ =UJ+CݴJͅM=cSMS|}>-̮tv2FLZ8\>.2Ѳ1&[_(>) 3#n4Z><+>)N8c>>>hfD^OnӳSt!MQ*S;֥ř̓(-e\k>{bo@YKsslv>7{JnN3t^΀=R Y [h+dcYG< =35i z W>N% Hx M=ǝ>4#bg<%(UsnpCq=#\>^;gUhRmdIK^>І~ʓO UdKc?[J6;gfJז=ΖdeߥDdK ~C$\d<^ YVNQD-^ĘQF=~ 7L+xgd3Zv_tTР%@&%UTU^ŚU+UmnFFfK-[gHy`Ӧ~IM'JXd`X5ϰ`ƍ?Ym"IjE˕kp?9OXrV#`;n~Zsq@pz+͙ @U@Ƽ{G_$q#g>}˳?@! ,H*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sH`ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-Á0dDjp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o;@;~!O ID$% A [Hnh*0 ` 7DB&B af$@ڀA Q$~ I/| J/ %!I! `@0`_$ H2hL6pH:x̣G7 IBL"F:򑐌$'IJZ̤&7Nz (GIRTMT<+cIZj.w^Gr0IbL2f:󙧙#Ϳi̦"=\즧)rq&:v~ @JЂMB˔:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNw@ PJԢHMRPn LTJ€$ANi HXJDܳE ӂԞ6*VூAJHU?KnC0tTֺA]@ FPFc[ ߘAB`X.VqN$ޗ.ϯbDn/[HXϊZ:d-<+nHh-7ԅ.w{g Md>"AO 3p_|÷lz^؆|+/ H v؅| OƓ-g[_AAAdp $5ak"Rx@B& )XB([vP[D w6JBDj]AN@ìHA5m{ZoA"<&pecq[,}0 B 6pko$ p ;ʾ}tV, KWtM dwx K۸k}-Su! 9H=e1֯7␯6n\}*p9A`b,po}X Ho1ւ**?/ViBDVVt7<'l5VY=VZn@~ <l P 5Y$XU#vdT! ,H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧP)JիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻNȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmx 4lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o菿P I>'!I$ >$P _ P@I@$' ~$  ? 4H B pBE{aF"  /("͗$آ HAYX̢.z` H2hL6p6H:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIK̥.w^ 0ILL2f:Ќ4Ij-Т .n|߼b>r  H :nZS8 | @JЂMB~.n&xJn|ׂmȨGґ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:TJժZXuTZs 6܀h- wuqV}k>$as]^`UGbXvDbrKm# g%AلaI`ִ J0탫iE+ M[ ha֬PhoJ¬HBk9n[i㬛mF܀ + ]2D muwV=He0_\׬ύ~n$]o mh  L w7lýxv)` ]Pƒm$;W߸g`d^k\3G=Y+obAKpv6we0Y n0Ad`hƼL} lK) A~qe c%lCco!.fWsLB >@X! 3to!pY)mx0j.-nB u d@a heM]Q=hId}3 fmj9{JVb.%,͸y6` ۠_u &A@,hZ2 _X r ,?xC-mH߶EpY N<9\")wYkrGI};LAKۂnubނpw+N2ԓ= rR;-im0/`'9tFnlX _ƒ@ G-f wb/ Wu%n` *.}\0m~q[e[.ng m/W\^/` 6U_^fEr bupMiswXV`F&<%7Faߠ. 1hƁ[Pha 03pb.k2v5af9b;%` r ,6} g%Mh IBL"FraP$ILnRDyܢ цlB iKC" $H?@$6Uj 6:L7 a-lQrIh0QB7HfJ`o (A: ҆_z;3yJIԳ $"Ovx&)ݠ{>/?Ί~Sh$Nx꒔&vx lTe$a $ ;yxJq'A>Z}0=L LBISh@$@YO:(%Ji @S#t pPxͫ^׾ `KMb:0Y*P4/4Q-Ym$%A ?`fUghF\2ۂmB*n)]:Ijs ٥-TN [D@P *u$oFb;@ܮҵ72 -HĤXt/C ߴ zR(+9X ̕CA-[H$]:$2MPNv ^872{,![J*K pq3؂ů*p ЄRWB` oD BbIݽԸH \ y[䳪ri.3`8'XK8Y~RNvM\O@Q#/Kk)|pYDI@BKOWjcDD*BR|1*ķn%PpoZǥujYaDbm@`r^T[ jnc+s /CI@PA -KJ,`\]-@ ϮU7\`cp8ّ͹w\x8C*mm (.$)(@ \~~oM(@@u\:z#r ;87J#u#@.#j it'[OKh,N3jiӈ  )y'5ԟР7$_d n$YVj%Źko{!*_ (K`T1*;[DBq()4W {[wmTN!KEYqml`gNwT4VGW&6I`uAK/Uk/SzGZQF MH0E 5J<jPsV8v|q@[&7wp傴!` IIQAß3Ch5q?Jn[ EHɺƪNs ry*bsc E|@f$c4nOA%lT xilS3CֹKgH!m ` %]c9qc  @) S}w$O3@lJd D6&cs B8oM!d4:N0b9 p:CF4Y8JK98>9R0v0:fZ+>;U9,Wi*vMAR}HG- P?{4E 4wdc\K?䌨J786 oğN7u{ (`_E2N ` HG ¢Kb>%0w)xG??G9x7CuEk=* fOwT}3*`: 5Ca \G1Cn R ^L:Ĥ hOl=.g  vCgL?d dt>E_ P[FssuX] }=5P=YOFH ;'DAdĭ4I=X,D(pU.-q'kcF_kuMBVah~N6=ínysFDT\<(œD0iS*^=Ft \]=a% EP `eJ,VS̱9 K*2mM]֓N~ߐ ei~n]&V& q {(޶ctAc- %In9>U[$KBa Mݛ=@T7z M4? >^msa ?qK]['ٹDLՁHƁ/+>R5/> ՔJoZ `Yج>X! ,H*\ȰÇ#$ ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸/ͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmw$uoRtmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯"ɽ$$mH@'/ `` [%p? , n68 >0$ ,> PhAPX6ЅSa$nB "H B DBA d@IEXP-ܐ2J"nJ30YBH(: > IBL"B|$?@%#)L `H$ Jq  ?ȨE `I(AIm&[@*`ژ!(%A[h&+ =Nr4$z(Hr H.J" ؆1BIz -F>Y"`e nR$%P\SL!Jhf>j{6̓¹6RI7dHD񒷈6 c'6YRf"00$DIF"̴9Mv~s9J) ɜ7f&Y) 7n%YAF*ϛ]׾ `KMb:d'K Ae7;8dD`] "Tp ]-PQ1s:t\88.h/"mtO{Ն<GN.+' rn ݚ5SK%jւ 6Ъt9-vp .pgy&^7 eEH9IniAN[PqB3D6CR%=? u!,iK*Q\2=-cOL@v/46.~ x ]`EY,0V p ;  ]ޒdIݐcjs.U へi6^Yc-)nC4(*.-PR0a8óB6Z6R^JRR _K fy+@uq ]X".vp;>ζ-q)6h0M/\pB0;z#n;s SGƝ8e%ߐ 0qX 6Z;lHnAv7lo#Ayɕ^.Pn߂iC6I]|nB]dp崜']8j90w ;8\iqPe]CYe)D p$"oХ݂ EyԻLz$MGe$ 70qF_7)u'_8 1g6PҴMeDM|cN@66o(] tH?} [>ʵoh )aph'h SX/9k=m9ܢ  ف*&Pz n1,;X!z7TtI%Pu dbi  |Fsys*s"yt@ E$WVjyd,z'0vxՕnq-p{ FdpJ6@%T^u7} qN@uv .g5@5[i \v n 4v!nvjW-rt.x{[4@xfe g0v`s`#hsB=|yxނaY0uЉgX].4:+EWouЖx̳6(Gh.0 ӨpDE{cn1y aݥ:($ E>~q㊟Nszц#aÏq BG'(G$s Fof9&) &Xz+ v&r*W# qP$rev,c.@)l憓Q! nk3.¨59fD)g_oc?K/qr؅pC:ivrm5o s.Q;1 vSk)8Ui7nq( ֑1pd3֘*q\U~ YdӚ$!Q^ƒs@@vHa `n :WSyx'9v6rאy%0jMe5  99FVuI'Zg@zg6:fFV8ZIl{Z8ZI QX@ZܚZZ8f|O$+kp thw$Q7Ou GZc ZvvuG{ruղГQ2貼siITkS$Ǵ4yH{=㵣W$ڵ v4vpu x"Gvc6o>SrG4.`j6$ #3al@kKH{0agGC;={GkvѢx& Zs{JF(ۼDYdh%G*GpAy0t%*T;VLH` K?N s&wU0'C0oh)z()p ָ0i˨ ?i1*p $\&|(*,1R77Fmd >*AS,sIQݣRSL+bAQ4@A<\BPŊRI=%ȖLlpW 3pW](LbNdyR0x)3vpojOa#T&eIpɼ!BxlD`g|/0ejfui3ų_*20 lwƳuPdeʖ :3PP/ t`,N=5 \$O k8PFt,ѿ_ߠ32 ( ]X.fv'=&",?<ѳTd 6kp r:/ B^  ] cM5a RqAM$@\[K=Ft`.I=2T[J,< Lc=jm3o q<P}em e\,~]mX-S2_m ϑdP"YxSq UNw܎\Ю-\^uIm\EΗlP&uIV@ 9JKrm-,QPb9_ xݴ ƓJFz Kz[=y WP3p. Z~,E,Kx c@ͦOT|=D\M<IA .O `HsRtMd\=*u[Ye۰9łWBeZ A!N>>|xĽ@!me5,]޳I@K9g=?:Z_sWL! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSNJb!հc˞M۸sO% N8Ɠ+_NՑ6̣KRkbË'|ӫ_Ͼ˟OϿ'hwYg 6F(Vhfv ($h(,oR 0h8<@ FDDiH&L6PF)TVUvVf\v^)dZhA.p)gnixY`#z矀XgMv'cHcFVj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf2Ii+rUnQ!i+k/Hrܫ/_j l J0G,WlqOuw ,[l(,0,4l8<@-DmH'L7PG-TWm5|\m_'+`@6$'Y^d7m2 ,iBwsw}o`]PT`iCףc7KCdWOm F+vB?y:謷.@x:@U _'|S;Q/Wo:}wOއ觯i~AеOo6z HL:'H Z̠7z GH(L W0S(2áw@ H԰HL"A&:P̞H*ZULDHp@"C1be6pH:x̣GQ:L4K HB2%l;$P 3*r\S=Slȶ8$X2)cKZIJJBYA2M|e\)ZpZg^bDژcbr~ @JЂMBІ:DIr͉Z^ 2/юz HGJҒ(MJWҖ0LgJӚ6ͩNwӞ@ PJԢHMRԦ:P 3*UDAVծz` X#ѱhMZֶp\JWPx]IHB$],d+A ^mYJJ@>*\ [ Tfߒ7l$l$ ;Z,V RYU"*ۆSln6 ul6H" n*Ag+Z榥x@NY%; ,Jz-%I`Χr8~LNmJ";[6gp &BLbЯL1AA_EpX`,XD)cYes!j2K#7̘%S(R6I.{+2L2hN8,6s"nvL:ۙ@49qPNKaZ43aE+QYMLU vtF&&|c!=`/} z^Gi+[9ҭ>OBDJ>ȡx]?׉ɵj `!F5jZ0?lS$öXlI4AZ{~[a m/;n &- KO%w &p$R5RVdH@x ϸDq#\ NE}dgBB.мiqo."b9S^|!7%0G1plG E d%VzE]VG!O bF[ R $58BBΛ8(a:~jӹDx??).7hNQ;z  !G˴zpF}"}_rRwGA<7FF%cQY^h  EfR;p3otnh!qwp6'X2zqng'zWhr$y^(yRU(ew.7V)Viag qrYI44(&Hb#AlsW1~$h(ewG5#k[ȍ!cuØVxs1H5i7qo*µVSHaᅛYr 1̗&x-1)YU1~x6 !}v8SiڙXb2&sJq ) #Gu6Q 9TU&֩sdITzC>Ǟ#I Tyրuw׉f $'W)2* A1EcdZr(!# ]g ãiU"Q'(8:0H^=iX: QlPUBn(nryrr8z!\lӀDUѠ3':T:4vy͹ [*Zt aI UvH<1xu;!eM`MsVǨK%h-zdБ da:Rkzэ1:(>z}XHɩ*%ɞ|ʇ1ĕC.ʸKΒ<ˮP  ,  BWU~K2۠\ ]h| М+@# H@X%^nYcJXs̰IPHH,Pf͑y?v,Ώat]s 6 e\ ȶ'،tfp ,gL8Ьf&=o5Яsj L8߰ {p30Rpw }p(&m$y߰ s ',3Q-r@?gjy,ry\Ȳqh=20K$N\΢Ep9&N #r׶߲l^ `..կMj EL}W1پΒ>]Hq:d *-p%?ͻ! .IP-m2I#ېN}9n )*bRfTׯS] M%ܑ/Pڈfvmѡ|FY *i)sp<|=al Lߠ.  O-v!:?3RLXќ ˏ/S s` FQA/-xoilcv~m'PҮݸ/ ~/l-f\m]ͼ,3@fsM䡷A 6zU9nR'|'\!WoJצ}|CW~觯x@I_,kV{O HL:'H Z̠7z GH(L W002WwpY H"S!ķ5P)ZX̢k.^ (2fL#%pH:x̣>Q'lT2?򐈜#$HZ5H&b&mS 5A~҄MET#ɒƸ1}$,7YriBL,P򗽋F ^˪RffD4EiZռ6Aͼt"Q8IrL:v~ @JЂMBІ:D'JѩEqb̌z HGJҒ(MJWҖ0LgJӚx(NwӞ@ PJԢHMRԦ:PTj\R3k*h:ծz` XJVq)hMZֶp\J׺xͫ^EDbO% A VW, JI!~Kcm$Q,\HH"ؓfB kkUIѮEQ+I@Z)IH@H5 * ۃ6uK&90*}@6AU˗P[I³nv'˲[6$ UoZPY,WUL` 6H `4x H0 # p7{ G SWm(S)L~1M<@ZmP:މ-\Y 3^t'a`lq.9 1c2m`^-en&]~_5WaW#,T0%@c (V ;0yۆ9[{gl hp jAC9Z J@9&N~\Tc1I_W$B++ tMK|GX?rnCfݴL/{ 'dηNΰE~;א#N^3Ajqmeӛ^0Ʃ mml%y~ 1#w M/7UZ4Cl kIuC2H7&Hk)Z" ȵ~a ZjMpBtoS '>Pb7, qédP$`)@yS7hja'wrr"!jÁ^_("R C$Qu, e=]#&ܰQAI1;iQ, X^ ɻ|\U5y_ nuꬥ0|.% d7yR Vzny G%p )p *c(zvgq{ anrGmh·%n`]2\)xUit+uNt Aj~dUmXuiP|P:)`p svn>'aT~a(WF'V%} ( E3JX1u^q_8sB_n8tX'XHX$zTH(`#eӵ G`#Q~舒8||UxQJӆ$>r舤(g@hgWwd؅Kz &6"F{ch~Ktu߰ Ch#vhg¨&rrv Bt **PJ(("fmg&Wl]6op (Ud`s> vM XW#xeHF%{6j f''(%Plq}. r X, w,) eIhWZD'}`?0Ip5cqu ш;r`*y^ U IX x=)v`7ixLba8N9$ l{@gfk *xW}^"09B{vH!p} Y] ``ٌ6I2ekwy"j9$ ##mt#-Wu (zbwxeXcǔp9_a 84r vmYY'6h_b/I-N` r@#j肽I`&`W{Orh0hxqg1F_PvaIa79c晙DXȱqV^і4r`Ua CH͉ ٜx'./XvrI"tGJ ` |]x6H]6!xNjE&=ڥ$J, ijyy!y6m) $ v`uMC@*`6Mc"FΘāe*Xj)"h(n}^'?PjyH7AjG-p@٨Jr`c6nBxyxz:$1"6-Ihj X) ap8|F6V?|aoE= ʭ1#Eq76G*-$S@`!!z) ;°+A&'i"#=a_[:H0 y#/pףJZ` JP5?+Zag &S+RW%x1xز6& )K2 ^ | I2+mk|x-|o&I(AIb;^h{ߚo[ ZQ)KnPda`;6!fӴ: qPM ЮFW?Iи2Sa.0rws`+S8f;` ⸣Ea+oI#VQ!H1T!a!VXa|ſ""m| fjպ{`&$Xfj!۽#ն-/ۣ'#ĜDEN vF~]nj)ݍ x /@1)])g N xs. )b>-}=F{D-` psvnZ`I. uylXK敿ce N=njeڎXg6Xwz>ᣎ~% :ށw qh PF_>nXxhI봆ij s>u=>4ڄ-jۄjkksn9}ma9N۔]qZkyNήpu.|W~`Wc~( (}ɉwsuD>EY4)I)gk )=`MNU:^J[v) vОsPnӍ S=zg ܻM wƼO'S݂ "$, @cX؃^F)@erY6nqm~`Ze895i *DTg0] qm5םg @|! ,IH*\ȰÇ!Hŋ3jȱǏ CIɓ(S\ɲ$.cʜI͛8sɳϟ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄jr袌ƘhF*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+VknBv˙$k l+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7.OTWmXg\w`-dmhlp-tmx|߀.n'7G.Wn9 C*u砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GHB<(KR$$ ,IHH $\ iR$Hn v8D ?l@ 萈*aDT,i`ā(:"SbI`r$HpcAV(+%$($;P vDc0D?$ `ch5 Ih-$цH퉍 ILf$fd?IRL*WJ l,gIZ̥.w^n?r Ŭ1LRJt6Q& -Fn m6)7 !$?"Ej1uH n6h-HS #E~R4!imHI:F̎iC-pZ$@H 2&d(=sc%Do1H$aAjAԘM-A7St)AȥcJljTot Fݐu X+h bRR0T:QIEjTf$$դ1'3:d'KZͬf7z hGKҚM-1)aѓ"K rֱ-SKzVUA)E'{VO]q-ۀyUy* xt bG@ue2ķm@lLJkmn6%66'߈7,md 0 _AYFy")lv_ mN3 !r$n3e du:_?b,bj)fXk(y22醄Ӡ  Rixmĭ[: XWLuuuHX2t|!?rZk͜N+_$LD2u]-yr Y0KX2Ӈ^NpaiY rʰ2X &jKUcuS03mK3'kIeN$RH۵2BpmT v@.vw}֮RY,O7#k'Ln\usc.Ur-+S3ͽ[j=B.;N[8./8/ɜ\rH%]ġ؅f`I8PԧNŻX1 H[c^])yYKLUY}c[ʖ;[ȠsWuw3ΕA- %D}g!ݠ `od;  ՁUr7 i7Ρa:.  \D+( N4Dh}A N Kmr?bj-p Np P;%4ѡdnښ*x rNpSXʬ?|)SXEѡxGᮩUn jتaNV j@P=Na#ĂYp )`SN&xYщYjİ !,?FYcEQ)ߐ+zYkvꭔUճD;7%zY6 ZY_z ` ^;Iء *GKiw-G ۶D4%w`uu*p*epB:HoXpX .L*Ymv1ʨ G봾DhBYm Xqѣ${ۻ{2UULO2J} l5oy \|w *Pϛ^hKkp;zS VT(Tg{w`omw}H;wovylOlyI)ǐ焂'ye^GH+ ?{FL[lS^"(JoxS+MVqSI$(8| GXœ޸ {p30RpKkB s 3 /0R <Lw1 Pl y20 (^C|4J` /Qe2߅p 0hl ƞ\<. ĉ~  ې1<M3 %lK5(yTy$ 9a|[ƿ!4(Rav1aa͉Lmf XA.tS&ɭ,} \Lfͼa P 5GRu5&[V[Nʲ2 M|eGXˤ\H94GS\]Χ>ve\ rp &]tpN64|5?frЋLJo`J M$^HrRAt[r| ],5=o"j̋`Yh?LMԎ\fR tQ'tU}jLäQliLu S{-2CL\SW!=]DWÉUR͌Ԛf@Wel]H %˱{8c 9*V tL"SϨ5 | 30e3 I@T}V;\Ӭ4SUFEX\F! ,H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧP)JիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻNȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmx 4lp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o菿v;@$@Bm@IHAi$POH"$#@IO  g }LBܐO;! ąޫ7{4  &8>Q "a$>}IE/ H8.z` H2hL6r?0G1!uG7H Aq |dmHq YK6|c P$mH&i# |d(Hp #"W0BƓ0+Gmc*y$ $HY`4H&i<=jmIrd4pul7o",c'>]~㭺Unܴ!Squ+ CMw]Mz+@ i.\-% Ӝ `mZ_|cYۂ!@}WXbӻ%zL5 Il'˃ Wl!yiG[@%ȓ +!K.ՇK9-qo"[6 Ki2D6<ѝË#=bo$8w@=i"*bYR gx țy`, a=[o[%Ob`+9o~-uz#.BHqVm; $.p D+9=mlHW@ hañz)Um`cHb\` 7r헤&IɶZϸ7N /ЅHp ]|QB^@)@.j-0|tsg:!T"w$5C$_9JmX]B#ޒ[ VIs !(jOyw|}Wۓbn'G@n&.D `;i@E- C(6os{zk~=ٵzdf\p]}HW| IMxE|&6 ˗z%8 #l`RޮӳT(fW)4Nh8OFPWQ8zyjvmPddz/`i5wJ6{.pwgczN>go76[vjӗ9~E}T|#'f4G8KiFxF\9+wV>ͦxnmG>)eJ d>0{WXJxvH9.pP) pmnJmMaWMPe7<LjNJHp+%\mvB#erPW>zh>z>GX>~3HTNz*l惀-pH.vh>oS}%.HOH't>vK='|#Mp@x= u WzSH=*h83糏8* ِ9D d ,=`C ? A3_*ӴOt\T+9? mph+x<0J)4QL yӓDNCv<%^w $MYGTP2)M%JՔ'beyǥ ;nI.ILvu)M+;Sy0$;&);hI% Mwiaq N|FiҤJ)M!^ i;ij uGyDi9o)Mb wp3 3pwa&КəJӵ` 3p/v`fy?Y/EuGy?=wy5VIIcargt\5uR`:jig%['3S2*zi3I4JoyR:iPWe5vS.4 ^t2P@rТ3kQ[1ge S>)M# `,*p[>:N]/!BU?OCH#¦D]ZjR4$M}I9TVK.@DOhj g*`2Tp!  V)MAdt@% .pIbj ՟@^ ]$MQG5NAHH8MW)jZ.ZQ[j w ::*ii9e LԤ Aʚj.*DvYihr H7*6|D@J8I* \ԪCꪲj :ƫ0AHPQ%M.y\G*ܪ0`lxUT[ly;Ƶ堟jGJ{ezNOx$ic{-% s?SrjAH2d0ʵR3Mz fyﲘG[١t ʭߐm` s0p BqٖJ[5*2 fU`[I{ث0G Ѹ[95y:Lj))M)а[9J HS`S wu H \{ Q`a؇S ;b`5It&;Epv)3_HLD0D<hH ˚D@»GQ ӓ! ,H*\ȰÇ# !ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ_Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmh BlKk-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯v?@@@!G"qHH? r@ ( @B G`~$ІX(D(9ġ  r B H*~HX}(%R2Pb%x@ ¯_Izp@9ṿ#r= IBL"F:T? HZ̤&77[p (GIRL*WV򕰌,gIZ̥.w9[B-nlL2LiC@Lm,&A)_Ę鳅'Lis']I3!lf7 ͤs07 |@G!$H%P y(?>>45HQ$a $%n ~s ڸ7_:m G+db@  V L"ъֳh@: qZU5SoJpJRhFla2:M4f\yӗ+[ZlI'Ffctϱ lF*UDW*[lՂl zؖp" efvj NtNjaJ ,.AQۘ}ꬴUmմpUa=yLIt7oS`gխ9D)^ bnW $o{Xƴ@{ p^hA߶p0Hx2_oI69؈T-=^[zpmT|c%mq7HDm&,ZzbUed{ˎO&4×k\+$hN6pL:9URyih&)lZ+&gD$ͰBdlI>ԨeWwڂ*{gP(pb]V~HE Wj!4YU3W}Mm=}'F%-ӻm@[+8xwX:5MBKJ.=5O ҆d7^? Z0K7ڮ!i:Ab%磇 `xn}̉MK`H$$K \.Mt!lL{cD/An c X^%&Ov:ŚŞաCkU Uicr]50O_(g@VIҙY؂=쩍Icn7l2a>|Z=;1MT&l6[I[f =YQ֎v/}GRSk'կnab֣-L b>׆a/*>Ij3`&U3bea3ԾE/WYObwNP| Ј՟L@Z(C4x ؀D~ oEx5J`GG=((MPzI$?Gx|anz$h,8Fc4x8:j ~rԁ<8DXFxHNPR8TXDzyV؅^`b8dXfxhjl)׆p./tXvXrw@|6 qXG⇆&؈Z nLG~ukgXHW>d VhXGn`U6nIFnsLScpR%Pu#WTXm`fU\ރLTX9rRu =WXc&o؋tkHqY] !Uu{sL\e܇LWXSpL߰ s 3 /0pR I&U|Bidžw 30R0`9M9Г&GZ<Ykt202 wz9Np .wģ ٕai$O6i T NH vf>ȓ׈^W& d)fٔC6i2p> ID(0c (OG6HB6d[ OЀ%@2H0k ۇHOn ̦6JB$l CIqp3"9x̣> IB !TPR"[h1?,8R>r HDѻlH(rHp6 KN2R ?܀T `.mr$JpH!$$KIo& )i@毗@JX(MCФ$ FSFmFJt"씽A$8LH-)BԤwOEs:)L|B0dWR,%U glc!DvR(4(C DXRR<:D\ XJֲhMZֶp\J׺xͫ^'PB-ɿVlLݥDi*ŮDAkÖOA*RMg>- . +[*P-\piTj$HUɠWBzɹHBdeg6@H;6 -]N[,f![r$MepJed/+ŠW!@ npՂI6(olIP>:WRB-#Z8MgH!qOQj^=,gԂ=7єm$zc~`vA]ZxlhQMp 6(bHغXЅ%lq&@h.ReTq]|,$ gQ6oϼYgtZ|`A3Ƀle\@Wt!# PmnF@SwgS[upSEXŒLR!QiXfaZtP.pz( qRp Ѝq8R9f߰JNv !sꩈ4CI^PEi(-R0Rp$4Vi(י` p>j-4+Jdr!x Xp64Cߠ *.Acm?Xsar*B:Eh)O<*/`Ii * "TaIpeZVǟ+ra S`6R[ ZPs0E30QEIIQ (y*L4i(~['0!ʩ8J7)Egbu&d7Jg0! +MPf9I`gיw0:*-MZd0*A Ng) ׯ+!:Bg WqJ8IM0TO98;T!?O ) %H9k1Q! `@r Z9Xs8;> )=:.PR)P `tuQqWXӵQP+I@DWxaR9Qp[ƶG+9N04W _RPI?Hq_4<,a eYA* L ް ){h[J6q)Uã %Z.pgP-gpvP :[t2LGy.DȗC;? L3.uZج'sqj$"\@v0¶5̝3ѦQz4ѿ!./;=3M$sӾ]>= Uy#@. e-1 N"B7FvٺA1`&"[[?\r|0^ 1K | )ppk/I_EɡZg؏ 00 @с)`6tH PEY (-%_7¡sPGa0mx gNf!F/ 罧ԡ6zܺ/=J'q[0y̟-l G9NK# 7VWc!~Ⴑg4wPĔeS3JhT>ɇ/t^d3={|اy[I+aJ0QHjjz [KWw 5Dj{vS=P _@SuAI% Pjt^p 暞Qn.tq*3 7d&&IRUH4g./:5mhY@6cWiP O~0c6/t{M/`rM5!I[I\e{_.Gs0YPU ?!YJx-\A0K[~sijm/ O|.vYU/Y4"SfA-k4Dc QāNgKntzcRv&@[>QD-^ĘQF-k@BI$MtDHϡG5męSN=}TPE)\XMR >[rJ"D3~S @QeZFB$ m\*D)^}X`*os"h"Hs+1.YXhH,LI$+Tw&iڵmƝ[hTN\p.~c w Zψ}lݽ^|*J)Ӓ+.f 10@$(`.~dzAsao@, C?1DKI=&r&چpDo1GwO$@]T^c]CFJ+r'  [aL8vm$R˲M7߄3NJ3p$zH4PA%l)"PG4RIB(N.݀iRO?5PNE1 ! ,H*\ȰÇ#$!ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sܙϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dm0h!En-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯췟ǏH @p_B $A J hCڇ6m0>``A(R@5( ~$@8FK-LA sC(H 7$"LH npa"Xaj&mHG"M@6pH:x̣> IȁlÍ@"$!! $p"  ā۳-~M HV( p@'(IWZHB([ !ء6pamsdKRH`"% I$dX#sHMН! (rEo;4?Lr8@<ncY!(!iKzۈh2 M2$xDVe+&’UH/3{A$!@j $lZm,HP "ӞHMRԦ:PTJժZXͪVծz`=-)[,ӒA(Az5,bŢ"J>$pX(]÷S_fSh- ӊ,Thk2oXT dص$ jKemϢ"sK3&p!&#˳\ng )Ҝ$H.Ul76|lpM\$F gH$2]';ױ`-^l,D+ W<%yxL/%DEx2eoP]3mtSr7t&³,s߼ptKm,tH.v`a!8irH☨(y˂&zQT1 mc@[=)BKЂː y/Ĵja.$"8&2 Pd-pdF/SdXn]jØR.HK U>nwhNH%aMIPAʼ0ٛkO-gkR̺vE J].hBKX{5\ #blc/[`̐R{!O!;Ȩ o=#1w{rԖcMl]&sۇ;mt -s98l0?ƶχ>9DO:ލtI%hrq l>$( y[REqwz-6ֵ&.aD/bLPX}aLKJK63kDx` @J^&m:TCKDJ` NB`680}:kDH1>K`_ >-Ra D_#V1 @ K =]D@6%6qItnf~UoXܧ7.Pfe}mc'5c`o. S Ry'H$bi'kA~ q A6+g)FffM`~$9&s nM`nv&%yw'}#s*/l&aIBMϓ%lMP0&@E 9 'q ˦ur >:`fNf!%@l3WX&  R?Nu b Hl=w=$'lN qPr0uX/VH9*%@diXh*u. 0 !F 1.Al7 sPf 僰Xx9F61jֈ=(ffmoabBG:& rP` s0i/ -0fV'RTШ.*@B%|@1s mr-W: ~61o%-79 +B#,9s)M$` !sV2i=Y:J)6S]i. }#F@w9wGlB׏3j) 4iP-n$sAyɢsok3~p% M.pPN )3d pPX?nn&P>R6Gm>ﶛiV}&?1(\` (pE>ɇn&vV>wpYNYw>=7Ȝ1Gowa>N` PӞiyd֠F89vv1XjsGɐfdy#&`nf> :omd3jj)swn8oZ3i)J(vI>بM:>-X9.hHmdoQIU>8hv0)ew)Su!I15*#mSJ0GDIp87dzl JGgms q@p/Ũ:7zک:Zzڪ:wګ+:%ZxZ-#! ,\H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ ȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmh+'Tl-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯YP D $$@n{_la ` IxHP6H < >" ҆6D%HI[A.$HB$H$@ᯆ9<>p nc ߷A&*L^Bf $H<MV$CB Cя $6pH:x̣> ! d b %D?lECn`&r [l2FWmO 4" P:PR?n1܆%Іmp Fv$$HrIhp e jf4?"nv 6H`%gr|@{) >Klg2UP{gCІ:D'JъZͨF7юz HGJҒbEҖ0LgJӚ6,)boM: PJԢHMRԦNT1ZX.wӭ{%o!CZҐ/$A$NI@r,+$Meޢ IM =&] % 8Ē`\`"UIǼ+ É3KLgG8 x=m>iL[жyMa#r]`; 4o"MpVy@'H $!n&rp?VUío ?CD_ ɖmӈZbր4i#M̮R.oI1,d^xJ/Lg],X:/+8z85pL:xIԈt"Am!ݼf%4"Є|>%%o9> F hnx{g% $ND5AX-K<֠foAd]Z %$ 9Z)Ek@ ~.uj8 @mSPG1۟7m7⍼nL6~q$A>@HA~>Ԯ^x$.*8ݗqYg}HBp(OO.ǼF_{)ATw~Cs K $ Sȫ-ge9.{}#&tQdkS@4+l=GSpN_Q{\  ؅wFT]qlxCU񐏼'O=kS! ,&H*\ȰÇHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMpҨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l' 7p  3iؒ@ ׏~rIQB$Hz'G>Γ< @ʟ HL:3'Hl̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2RHor H:x̣>D eqL"F:򑐌$'9P$"-Nz (GIRL*WVrEx,Y̥.w^6$ bJ6"Q8Ф_29f$P m]?AIp Ox5+|;wۛlQ ͛7Y/xuFx&L W0 gH8̡wH"HL&:PH*ZX̢.z` H2hL6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjV sinz 8IrL:v Cyܗo-~n/?DtϯtI3;Hǁ 'H Z̠7z GH(L W0 gH8̡w@ &qHLT&:PH*Za"Eh` ?hLxݱXxE7S#HC3N&| IBL"F:ET2̤&SMRѣ'6@f]9(iU&_C*Ir$%$/4a^FLV%{ʌFl)A&7 'CĩrZLg ɩv|gF)$z̧>~ @JЂMBІ:D'JъZͨF7юz HGJҒVa'JW 1{,LgJӚ8ͩNwӞ@ PJԢHMRҥ:PTJժZXͪVծz` XJֲ.=_JV p\J׺2)^׾ `KMyFHK YHO DYY5d%A Є&($H-j[#P%hE+KP$؁mjn'vhCf;ܶH-AHh @@&]!iB%) z \D Pw-@%K:ay.zQ"-b> e3{ G|CAqATLdL87^qI@Ldjȩ2 %;9mx2VdL( R2j|pLa땑d7ay!GnX'rfqS<2 (66jWoy΀|@:vkKCT1'ES(-.N{,4GMRԨNCEꋴZհg͡=ָ>m@z/5sMb1,m0";xA]]/dы}mwI Bm9=}^\{6 F7[#VnwgsɴX<&fr  Dqc&w _'p1U\!aᝎ1%8@/p@=cP'/xw{6? Һ}0 6-(\ *x?y 8_:IK]"*Lp9']T׺FNg])@">'+8#tԍGY7"^~;v~ iîxWi@0oy"-rv; ;)yI~|a_Hk[rQw ayӝfg,`v~j 王J>A*!;o#f8OW> G7!*}_ 85mF^^a72w 78{W"~{'Mz ~z` 2b zf+/X1$gt9!3^p+u X>xVEHvRR!n=&79( GX^_YiWhlW/G-ae"obs#8)`n[xW-H4s ȃe|88eux1weGVV*y-6y8Wc |Wp~WKz':(0'AF9'ngr~'@z1&x W8#v !yHtb'd, m\8zsp8xgǀ҂^Gvv VZ6q: 3WX]8uԴY>{gH-}؅! p nj-wz)`MFtA(ɑ@!x`xr8vuj`/@ uGGvPsy0&-i)]./yy Q\9aggR`U)W`o 7:2WKX9Y,ٔ[!`TWV5$])kIE)mIh03X(* {hIu9W s(9Y瓦)1R#IKrzI')Wapu&)q u1i@xœ9s99U)t}'2Ǣ8yV,vpȈ|.ȎȌS L+cIK0!$V~ @JЂMBІ:'8#JfM&ĬF7юz HGJҒ(MJWҖ0LgË8ͩNwӞ@ PJԢHMRԦ:%T7v&VͪVծz`]2JֲhMZֶp\J׺x5V׾:*~ˡP 1%bb:d'KZͬf7z hGKҚR[[lgK݉kα}WoKX۝"אЉbK]0?YIzZtKMz8ނ H|2/tW1)KN;F7"Lΰ7< GL(NW0Yv뉻2֮ kl>," <>&;PL*GKVf13.d#D2hN3Xfֺ` of2'y-8HR}% lȟ  -x%ˉtMNcşV0!6#vp1h:Ҡ6r{<6]Qz z[bZ 6[egΎ6MjMnwAMrAc<v 2oԻ &HM~'.im|nL;jszLR{ _iܐ(O2Wr09M.ۼgqos;e4ЇNtFH_ʡ\'AWp3NAtw|=K-|]O#,˄pd!@{w;q*O;񐏼'Owd7{=vf d+ ! ,LHI \ȰÇ#JHŋ3jȱǏ CIɓ(/&Iɲ˗0cʜI͛8sR$ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dih)!j曵 tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j+E ܪ뮼k*찌]B6F+Vkfv+k覫+k,l' 7G,Wlgw ,$|B&,0,4l8<@-DmH'L7PG-TWmXg\w`-dm8*hn-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z`ph GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` HF1hL6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRh.8*WTO,gIZU! ,H \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ IbѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,J T' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngjw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL޽X "H Z̠7z GH(L W0 gH8̡wظ4ć d0,e00#F7R1@H"vQ`3ÈŀAeD+VE.&qDė-2>^{<(G|dl(M ^GF`$8/ Do,vER?I<`37J~AS-ZlIb|XHR@ 4[Ap´Ql# М?0oB P4_G"=@@;%j uF" Ҵ'&i Ez^jH&Z-?HnG6%'Mi/[0ThD¦"}rH(|4,! ,HI \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F[Vv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/oȯBG/Wogw/o觯/2ϟ HL8URpUt#x* JP,? $ AEP.a gXQ qC`?ta'IR:)DiH&L6PF)NMiXfEUn`kbihҨ`l ti'~ܩ|~ 6*J&a6h^]>*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkf[Xvr˕E+Avقo(H+TN0 7G,K EgwW~,$l(,0,4l8<@-DmH'L7PG-SȨ]<\w.Vw}obÄuFT{}eg\.I=Í޻߂iB~|k ]&6GU. ^Y朏En !$靧xc nRuI^nogP/G/ԟ|gw/6r?ޗo!=~oop HL:'H Z̠7z GH(L Wާy gHäȰ8̡w@ s!1E<&nNHE!X!$^`HF Bq2+6pH:x#E$ŏz  F(-xi#AI&lC씂>tBI$%$)UNFΖJxeD< ` A&C)f2Ό)j|fF#nZ 8IrL:v~ @JЂMBІ:t|DMsͨF7юz HGJҒ(MJWҖ0#*Ӛ8ͩNwӞ@ PJԢHMRԦN݋#$PXͪVծzu]XJֲhMZֶp\J׺:5vͫ^eʽEAHJu*b:d'KZͬf7z hGK찦!SֺlgKdamhݪkq pK6,No N̍^+ݞ@$v-~ xKL/A;JĽ/zK7:~4 LQ0Iw%L [>d_3{ GLWe}qп2α" 1LHN&;P)xVβe" "ڲL2]>3iA)6#C|b9K\^TX4473D=0~nW8|GZFH;iGӖ^LZ7d9XgMf]`5)ke;{ ~Ve6Vv t H2nA7uC>H 2ԛ HbQߍ \N8qS ko8[\hx; $ L3g|-WGNrZ&V-W,L$gN:8y!7ns:9җNLOy6M2u WG` _gl*%B>{F.N_b{v} w7H'b]a.ս*;񐏼'Oʫϼ櫉9` JL` (1Gzi\Hįw.b}D2W+ ! , H*\ȰÇ#JHŋ3jȱǏ CIɓ(Hɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨSװc˞M۸ͻ N<&ȓ+jg:@سKlOӫ_Ͼ˟Ou7(@ 6F(Vhfv ($h(,H.(4h8S L+cIK0!$V~ @JЂMBІ:'8#JfM&ĬF7юz HGJҒ(MJWҖ0LgË8ͩNwӞ@ PJԢHMRԦ:%T7v&VͪVծz`]2JֲhMZֶp\J׺x5V׾:*~ˡP 1%bb:d'KZͬf7z hGKҚR[[lgK݉kα}WoKX۝"אЉbK]0?YIzZtKMz8ނ H|2/tW1)KN;F7"Lΰ7< GL(NW0Yv뉻2֮ kl>," <>&;PL*GKVf13.d#D2hN3Xfֺ` of2'y-8HR}% lȟ  -x%ˉtMNcşV0!6#vp1h:Ҡ6r{<6]Qz z[bZ 6[egΎ6MjMnwAMrAc<v 2oԻ &HM~'.im|nL;jszLR{ _iܐ(O2Wr09M.ۼgqos;e4ЇNtFH_ʡ\'AWp3NAtw|=K-|]O#,˄pd!@{w;q*O;񐏼'Ow ! ,LHI \ȰÇ#JHŋ3jȱǏ CIɓ(/&Iɲ˗0cʜI͛8sR$ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dih)!j曵 tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j+E ܪ뮼k*찌]B6F+Vkfv+k覫+k,l' 7G,Wlgw ,$|B&,0,4l8<@-DmH'L7PG-TWmXg\w`-dm8*hn-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z`ph GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` HF1hL6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRh.8*WTO,gIZU! ,BH \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ IbѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,J T' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngjw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL޽X ! ,DHI \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸bB +k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|9߀.n'7G.Wngw砇.,@Z! , BH \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k &lY  7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.WngN?w砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:Z `b! ,@HI \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗ I͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k 0B' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wn@w砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL&` 谀! ,BH \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ IbѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,J T' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngjw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL޽X ! ,DHI \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸bB +k&6F+Vkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|9߀.n'7G.Wngw砇.,@Z! , BH \ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k &lY  7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.WngN?w砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:Z `b;iminuit-2.24.0/doc/_static/interactive_demo.ipynb0000644000000000000000000000367614332717401017000 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Short demo of interactive fit" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "9e09f787c7d94549a40efbc670e9eec7", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "from scipy.stats import norm\n", "from iminuit import Minuit, cost\n", "\n", "truth = 100., 200., 0.3, 0.1, 0.7, 0.2\n", "\n", "def scaled_cdf(xe, n1, n2, mu1, sigma1, mu2, sigma2):\n", " return n1 * norm.cdf(xe, mu1, sigma1) + n2 * norm.cdf(xe, mu2, sigma2)\n", "\n", "xe = np.linspace(0, 1)\n", "m = np.diff(scaled_cdf(xe, *truth))\n", "n = np.random.default_rng(1).poisson(m) # generate random histogram\n", "\n", "c = cost.ExtendedBinnedNLL(n, xe, scaled_cdf)\n", "m = Minuit(c, *truth)\n", "\n", "m.interactive()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "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": "python3", "version": "3.9.13" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/_static/minuit_jupyter.png0000644000000000000000000011244714332717401016206 0ustar00PNG  IHDRbciCCPICC Profile(uOKaƟ )2(0CuB=XQ`V綻  /t:%$EСlj aaIe G- C^bDbV=y8wߝ/Ϝjui~[%uXgAĮ8L E|93Z;-WϪ)֢gZ8o_xz)]X[9HGi qC&?7M E0a" :B An2Q )_aS+i75~pyH11{\0T>+8bi;4X]m @v*\?'bzVeXIfMM*iDASCIIScreenshot:}iTXtXML:com.adobe.xmp 384 639 Screenshot c@IDATxE)]ݒ"""g؉؂("Jw))|e.޽weyoMygsΙ4Ŋ;'*"("(ZIE@PE@P?m"(""UUE@PE@ɟE@PE@PRJR֪*"("Oۀ"("(%ekUE@PE@Pm@PE@PTT"("(6("(@*B@_*zZUE@PE@PiP [;-MťK.RlY^z [*T C5k&v֚"(@H]P G}Ԧe7o^TqKr%k֬K.DҧO/3f6od͚Ug.9rH4}E@PxF@_<)Eѣ%Kۣ9s4~.iӦ *"(@LP4".]Z {ֵkW߹;ꫯJ>}dѢEw!ZZkҥK5Aɜ9sZjRP!zkNXAС߿_u&gϞ 26iϞ=6~zꫯd̘1 K'-Z+Wʝwi e=dӦM6| ӣ"(@F@5hR!<^˖-m9rj9sfÄ C-^8J˗Gp /Ȼ+>} )Q5A;ǽ?"(@ D@5)i/nΝk 7 ˖-Eѣ'sHWyv9oy,<~x߾} +Yk3=WE@H/y-]*E>ÇGa:$uԑyڰįZjxbd)ζmlh#ԯ_ߝQPE !/0-n@`ĈURE,Ybc9~xҥ{C O?g..~BLoV%ݏ?n(}}IӦM# E@P󮴤9s>bcgyF&Olͷ, ҃a f/?yxM&L۾(P@؈}2uTݕGE@P@bŊEvJe*@ olһwo؃ Æ i [c?g0!}Q !(@G@|$w%TB`׮]t 1˗ϚkGV|H](OEPE e#@$5jdͲ۷ĉ~#E E@PTT"("hE@PE U!/Un"("vWE@PTT"("Ջ8v"(@rB/T&-KB@5)}iiE@PE@JFVE@PE e!/e/-"("(qB@_Ȋ"(",UE@PE N(|YPE@P"(" $-ZH+\4kL *$+WI&#K&o^<(SN,.rVZɲe"a6mZiҤT^]:$fͲeK^WPDZY|yol[ڶmTt]r!~̟??YXX1_~Ї-Yw>uRD [qɱc|DPRq&wu@ƍc=f9sFjժe >!W7|XzHu&Փ~Y\}裏#ᦛn:@^~r= Hz)ٹsO#y[td̙w{I21XQGHv߾}UG)@dɒEnmw}vBVP~v}7ę>}lڴMc6m䪫R&_>䓶aa&\sM2JBzCPq&f۶m#Xҥm@gkYRyRzj_|qy_%I֬YÇ3V ;dȐ($WE <t+InXٲe'|,Hzo< L2 п 0 h<}(C? y3WNa-[vqOjVM$~$`K0bnEC;wΚf~z*Uțo)O?T:u$uʕeĉFyyqZS{JGQ!@VsW_ץ^j˕+g}?X}V6p@o,¤|K~I;v%~ٳg ",E]!!!!wqGՖ⋂IpZ ìFy!67ħ3ݲe1’(n<5 wL;h1>`,4h bjCu椟ٗn>hhU1@j $TE%E O-} !L{>p5{WX!X>Ç8w|xb;la&04u~>%K޽{e„ G&qݺuz()x!h>HՅlQll޼9ҳ.tl;DLP3!H/)R߇>:_(.D mۼyM?#K t~}!L)]w"ѥ#QFw%+ۑ?s\E ~ի8{q*мysKb1}Aȟ4X }zd'ӏnذ@$3!"J3UQ@?|ȑ5C˔)Sgn4C mLN.|1F]6>/ / mY[n]CsItdM@ [li5Gʈ^tJDLQ >ӦM<ڇ7x:oDŽG>SR;hF):F'A~`B/gZv]9A[_Leƍ֜0,'lcϞ=5"$.B ,@ ,Q0~Iʌ׿ ̠ &ŋ̝;7>Ni<;D(ȨHݺu` >3ZS7$wq(qD-; esY8W|&]xw/G 4yQ\X3Q49s, l()x!U{ժUdx㍑fA`dF`GiG1d:"6wAAo;_? -%+YV \{o??|I,nvoW "/elˍյ +nF"-Pl5ŖSDt&h">E3V+ϙ3գ"0ែ_9̬tF lhƂ 3O3 f%tt80i|\ Kه}d N8CH?O(3BFIY3d]?ޒGh#!~h ,|GA`o l-DmOP"@j@ZLN[L/;-d[ qJghjė2wBɻRf6ϗ/o74lf()x!^qƏUd 6~|Ϝsb%lq0>|xGA3t|VnΑ￷hW̘1%8eQҥ>6 .X@JXL}ܞ q4,b{D(+3} E@=HH]j|@~ VGzK.}^ ;:;z0e',BA*Vh}4JzTRi[T1KA 1frf`)stcNϑUsHmgV/"F GI$8LP/ƦψBDnIktW~ND%q)UE@P8(qA m\"k\E@PE@P"(" %qO#+"("PޗVPE@P8!/NidE@PE@PRJR*"("'n{ S("(@E@5hE@PE@?"("(%hE@PE@?"("(%hE@PE@?"("([Ĥ[7v%&MҦMk]F 9tbʕY|TPAHݻwÖ-[ 7n(SL X_'W]utI>9{1kN8.__X 0>,ؼZʗ//ƍ Fw.9sȟ.r*Uk퀶PaxW\qTVͦClbὼK3׊"(@B!Xb%K,QkV(Ϗ?.̚5 …2o<}wHٲe_~B|IΜ9eРA>}z9u=;wNx K0[[sw!?ӧOKt$M4Z_ڷoo~w,bo dp.|L0WVK.D;1c{sn9sb_dѲxbKIIEB1P}ג+W.;&vٳԫWϗ&=cvZDBɟ?ś<2ed+#7&Bݻw'"(@b /f_4hJ?<< Æ ${իW;z=}  ɃҹsgXA(~(-[,R==cUV]t-u]g̡YU>SKv%).`K]u&mr-(<矷Ϟ=ے$Kz{/p"k6GysRreA{HxOׯ_ǦI!zQiՍ7kGhTRdI)SGE@PE Q;a1!?aҥK d+]v^9sĉ颵ѣޅ5rDzǏI/G??\FeB`f裏K\yٳG S(0]#HL켯_>>颙E{vs $-/|<=ȑ#m1`e";J|".gn'Z[g4́.cǼ_N_pBМrĴoYXT).0h[ylڴ B`"("/eZzlذAJ() |5k&֭a0 &/UV0~RP!ٷo)2.\j/Νcxb޽{}aڶmkܛ?M^'x"?#<̉^_J}`RG҅1U.]w _B4RF &  X D6is>9@fOHwm}$ő~{N0q]%@Ĭ޹q{_!NMXS)ZEf8a\]^ ۾}%G>("(@Eʘ1c"<¢4R96RQ8)0tPk &hw Z@Ry-VtӧTXކyΝwi $ ҄44?]0 &_}%*h/r(܋V!NFlѢE4mjI 4Z,ħ~h5kִhܢNsǩSZc0d+h-!Br֭Q~wRƑrٰᄱ=?\=G ޻"(" CῘI(q EWSBpge&+Y9r$'PhiupG( "|4hё?h zoJ*Y@@0]"w~)FYvnР@$mqaC>C| S!aќBɓZq֬Yy0qƌ- /BoF600hDd='MGE@PE `#}L`գW02 p vDsL sA C0!R4hf;9*M3j[haɢ O^N _ ,YĒPVs4QѨ!@bs/ m۫1_-#Dh1eH"~y_~ z2:M9ȏ#=0رK]z饖`چ:ҏɚw@]\nA ' d:ޅc("(@?Hw'됏 prge@6bFg90As`VtXBj׮mxGLY0?FVf iƚǎkI GV,CyhȗoLԐoeeS@7/I`kFYRS&.O>-'(K%iҤ5{' !o5 Qu[ io X`uڭ]8V1;N!}pům>[G=WE@P x!TAM$폿U铕pg/һwo3m̪{&EjA\~ײ k =Z0°ӒA6 WR6*h@,EGY<iʐ!C,YdϩAV)RDЙ!?}QW6ۙ@Ў9{Ƒz@~1".,R@ua6Y t oHK4 @eti@r3.0.=#>cRfu*kE@PE !P4ќ!Ъ$bds(`E@PE 1H𑘕Ӽb-◘C!*>WE@P;"("(%w%SE@PE Pj"("$_%w%SE@PE Pj"("$_nЈ^ɷZ2E@PE #^Z@@5񁢦("(@ A@_ yQZLE@PE@P%񁢦("(@ A@_ yQZLE@PE@P%񁢦("(@ A@_ yQZLE@PE@P%񁢦("(@ A@_ ^ٳge%o6-[' {sU^  ƹl97w&Z,NaӛXL{"6-[+[0څޭ> ;GobIcD0l_}f?H{^kx&$kMV~p(N:-_>,03UsrH{קOAO|(x[Ξ |Gr(J2˟cNrg.1𗿐 Z3ߤ/t铧,;w|4fɛ2(@dAʋ@k˸~˵O.d{z29qԼ^'nhÑJf5#۰xmt/y4|BL\I!~r˳ؒ 2񫟥hRI88q0G쐸ʆ2"HM؊v ebS.J7(!\m[njFL-E*e{˧#sbg&]H|'S]N;!m>rLT!-|迣c&Es=2v` aLU/-uj"9qo|C?vڸ].mVKzXY1_ə/Ir)[g$G\־.Ovj! 1Iӗ\)_vo!$W<9h=O?!GLhԍV2f$h $FiӀED_$GbJK{; \1*7)Y)k']<"}&g|3un*26 lYA >ʇ\8tY6e{P7*M̷9{YeA~zw/YXF8gdY=(YHjk$Vְs+[}UO s$KRe]}I V׃˨7"\Yfg}KH\tM};}0EYe1.V&i SԫQwܟ@*6GH7/mAzUd|1Ѹ-cԽY*KlrĘy?ୠfmƬoәg;9/KV.)CISq(+e쥂 fZbqI(O~_?(}%E*'E>559pH3ڼ|\͗'=f`G\b?5_*W?v-Za=0eT M{s{ Wp@-iYAj>y #_h YkIY-q?s,4_ Aj\ {|7mѠeq+\6i}!ęTS߈.OJתhÖSKna5đc5@):D]_=%~-nk/CeIxO2e,U[Ա /n3d<`ٖwSה !S̄җh~[5(ūM)Kr]zTR=EcH^_Y23YfHVu1gl2J(/[׳ķj:e]pŻ"rʍb2 $v29:bR2z2{XPjq@NЦ) o.[^\-X {{ 桽|a8ٺj%VnX<[4<(U+ cV1fV3Ksn]=QbuI<hw1p(#/z|>ŏnfFVjn{A_ع?O3^SJU/}99i|>6҅&n.mY%An&f8=z ZM 2>ӆ)o7ܺJR {bNWQkeKs 赯l,3f *,pjs\o}C`"/|;Dts6f'&9+cY;mc?3%iRטν•drps%h!wql,<`}זXŮ}ڧwņdV6sG*Ѧl͂L$`7'iQtN]~,kw^?v٣' +UE %l "tjic<%5f$V[= $N|i :O cst.wwF!?9/ƭtaћ7ffNxWperiK?f$V500$HKWOR !µ1|1"%Y;o BVp 2ˣsepB}pA##sf wYoYغ醞wRg* >Qۥ_R }uy#w'7!ԙ޽9wkJv_7t]E؁`׆oʡݑ}mrw/$<CҦO?-cVBjAkE !XbQRhDԆCvsM|C"i ($d8S-mE3&*6.11T6\%>-e".}}IB1+(~9}:u$P: y6/:!N+!4S?R YUfq`Zqm/bBp©1$+ā҉(MKV##jGX`+ZM[LgN "PorzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB@_rzZE@PE@P% &("(@rB ]9z~^[,sʳ@7;d/9i_Pm~?$_O㇏ șgri}ϖ]2|W>E$5"8BfFDa\W[øOF5AmG,nxծat!lE+ ZL2d@ +f+UՌކk,t33xzo9`x]9%` Ak7@ʎu[-k{ҲUr ~Mw;mКmOeSJՖu<p}3gp0aSN1,;O}/ 3_:y(@ g_% A%C6䯄 ,oΘVd!CIUf:)VՌSȷ淶2++NO1B>}v]8/؞)_47"%7{Q1"8{5p@299k%[VneI뻮"Lϗ淴mK((/n]ZKzͼMyN3I[" &#&jE֭,Mol+3_ =9rLT!->x{bM(g|?AOG@Ի!9 fa,H:bUii{oGm Dʔ?dҷdˋwGm\V&y#V3[a&b̌IB9`ͽe ɲiEB/.W)#ՌYw&z\8Ԭ( :]fxD:OM]zY-=4句aqnmZRؓ60>(3:qJ4m\uLE& &$'{qyQPNlq s٣'[SA#ڲzo_6L@gYp}ȾRtjG ȻB13pCdfOSV{kn9 YO`ټ|]d/JYitB6Ӑ+ܭ(G ._ܞ{5Fw@>>UįjFZ?:1|Uc:'uM_}f_n֏Z5g6aY9iX2yx)c8SfMrwF2s_E+*#:Ԅ@x`k4E*0dLX"A؏GH1D; GʚA#2꭯m^?Ѿ3HtqSP-. M\&g˅6T?uz@nyzYHdjҵCXgݫ 1A39 $ 19;vr]L 16/>3nx]Kɞ/`~"rp~Y>=%-\j_D'@VsQA Κ?ÆW|fL2D$?iite5cv_ÉfFw[YZNe?KOYtdE3qASnlm$ls'#ܥ=>8B| RVKF2Ux}`LOHЖ1c.Uo/ify]mY3uE3>4lt8cB02 mLDr!Moj+o\čKA>|Ѿmٸd%U6?]e <Ңl4 cbio|/pVAUҼ%Do4Дν!' %|) A}ecu+$ѺҾ#.{(Clkt<|ur%=} M0JF }khtyV換aO7v% |jɲ@²h&+QG[z vR^RI7_4ڰ]6.^maM_D&u*"xę.jn?[jV.bߔx^J\ZhA쯯 _͝W];յiҦF)F^hW[f/1LhwћlƧWJ(aQJY#K#i md\ߑ4{%Gi[VpFEɪL4 ,/e&bj(Y%~I|KjTќ9 j7QʶLJК5[i--s^spF[%9zYP8q/NЮv|6K~7E;g+ m6g&떁PcbK8 2]̩(@B -_ͬ4$GO_SyV#];cЂ@bf,<3M:f͊+;23cf1fhӏhSϮ\b/ +8vh" x͂9ٮo_'Α3?8E@ kwvnN` yw–'ics'-{\@FandO_smۆ\p$tu|Y &L0Q3qsg /}P,6k(q@BoD%f:B:W4}8C1wJ\ѻ>1TnZCbpAf|?:eF| fg/I2mth 2@IDATsYoE qP8yZ?VO+OC `>b@fVuCece9l|8QͻxwVX*+j!~-hpnM`"fAT?f3dM9Dk!aIC NXHBӶvrBvĹXle<*sRN,"Z3wVDFc䅖h1Ϸ^Oc:|n25 Y->ps(6U\{/=Nsbf1%C}M$-7LG3jR?pK]Zy? gya[U~B93)Y%}^oV^o{1 &aH#3F(Y0m?66Ff@}v5,7n1 U3Y-Y?TykccLdl5%Trv%v&5d%&3դ,iu{`&p4Nvfu&w_m"}Bp@x_?7N`p9x+7hAWB_X+p[`&7NFLJ还pEn eVP%9hoNʨeQ.VL̀0bп{l͢f|1?ņl랻Ӟ;=:sbk[9 h Yu̬tu$=sGǔ~8> ͦB#|M0aگf8kzܥ>DŽf{=dg?H{^BsU91;pAxb'e~.G~}ҤI{!yqy#CK Bf{tq( @bŊE^qayhDͤzܘː ?b4|KÙE3,|D3y:aLqxS9>a+ lpJ+[ִ':᷉Y /gNg :=-{:BD.fAs/f&/v땄꿰N97_Qnɑ|J%A;/~sE@PR Re"("(%kE@PE "/t"("^w5WE@PTTҵʊ"("zPz߽\PE@PR!JRK*+"(@E@_}ZsE@PE@H(K/]("(%kE@PE "/t"("^ۿc,?C C-ON:of#%y9.lZVj.[w|o]B={6WeS V sgɜؿf-v&Ν / 52q/1aͼ2OވQ߆Qך~?!}vWJ{^;nع7H"hɡt߿,m-N}Rr)퍇뤒G#ǒ*:߱% KF痆<]ȍƶQ%UJ󀰠ڳy|l_Êԁl%}/W>E͟9}ꔌ~gp|)erٹ~AuL>UQ%`G GBeE枎aᶃ1}՝WK|"'ɣǓ(*B Yhz%~=s<;, F$'!sKߟI>,}_Bd`i5Z1:I6,Y;ᶃ HZJ%s,EkE !䚿3ȟHέ5}/p[Ƿ] Q!l|?o;Smة-V}2B:^lJ6mfē3&P^֪ԹMwI|CS _.'6w_cÅFYczYmwI{od5RLQip] [WuV ejV.ǩ Θɲzr)PjH*4bʍ2٘b5ArgϜC3 Z2*In,OLY2K5I4i|kFCTv$=kyZY:u,cT*RyBh3r՜r |i.}\ma,7裇2vZқ0'O% Oo'} RquirC[w{P!m 1 eVZ/סڦ 3(O_6߸/j)ZE HrnC쐪-jG3 ~#s$9yd@>rp~dvyt$LcуG[_`|9mZKo9ԒysJ5eyph)oe2g|p  sMl\Vx9~ԽlY>?k/vrd!}ć3'^5VseΑU0;h!l}~ #_RfWQ5)\@(נ0G#x[?N KV[rZu]}E#TK]BPn%21컃o[fuyCw.OJ%RNe)R{#L32N+3Ӭ9u6jM!:n1a|GBeYMྭV3˨RF9ep(Q5Dxղ6m`~Y3Bi !P^Κ]]q_SY<6/0Sґ~~[B(gLkh2dhT3{޶181w`5T{v1w7@VEw]sIBE0C5m)AncÏm2}O4\'b&>%_.XY;Eh]aυ鑴ziN7<: i5AEHl.0|~T18)ޓ#ްZ;m^–7s~[¤\wbiaN0HG5 5 @J0Wh XG@?%4}Ft1(+󴦓n|}k_[2aYQ1C3´9^[TMأ=FBC¼6f& =!9 LXGCPiƀ-;ɱCG-[}.5 N]hB9i[M!4-)m:5a<`K8 <'E$kd3 ?FQ|B-k{ѝRjCf6SHǏhRBϧp=QQcb>83>l,u樿,c@#?ڞj?MFmϠJ_A889{Wn1ˣ#f !\L$>`)+*WC36,j@XhBݹCB`^kCQj/+GU{lˆ}-8h##!~H2Uե) d>Le8I -^oWry]^R]H _Z)q UմJz?/Զ՛;G'VXl\3ѣ@۷Sj9۶.Xam1j+h 0wvrD~K'@SXaO5[\HGVֳ8XryM ?j%ZֵcI0 J#T{/5 F=˂DUF 5TIF*N'lγ E@BpN#3ǖ*0i]ڼKJobI/$Ooyib7'8 Ǧ5ΛYr6+qB%m fořxwy Ơw1!MQo~ciqVہO孯?hG]{紛;nӧ~ =?3{ڛPЕ[LҥKgθ_>%jVg}O~`TiV+JM@CPi41W66Zq^F iiz7>nwL &L1;I] 3M晳g;dLnxnZ}i[؄IeOMH =iϿ\N--cV&2 :l/P;hٗ֟mc,0푢{_V-c]t|6Kņs@` ]ťCcJύ2V׻4,e3۷9֨R`m6M Gv+X&핅nP69; >Y n1k m]x +Y{y/\1ɎŸAPE@HE(KE/;f@j"("(I^sVE@PE Pk"("$J{YPE@PDG@_C*"("t(K:5gE@PE@P[$z)4CE@PE@PDA@5f("(@@@_xZ E@PE@P%f("(@@@_xZ E@PE@P%f("(@@@_xZ E@PE@P%f("(@@ }(F^+WNƌ#gϞQFr%ȤI"Om۶'NȔ)S *V(?9s&swXboK߾}e̙v9RRJ?L͛7i&w/ɓ'姟~"[n-ٲe ZŋKÆ ef{6ݻ믿^Ν;%GV\~JQ`A{^W^yO^~[>@d"7 3kPPukKNuuk?3XBy嗥O>hѢ`¾M7$UV>(REi0tkl!mNT'|R2e$}ٳKܹ%keʕ+ْ?Ad2Rߣ>je7oeJ\,K.JĸfUP1ۥ]vr=D3xw_R2qf3>S ,Y$Ty8uK7,mڴ;^ĂÇ[_昤NجYZƏ/}YR#Q/P,YR6l)nݺk%đH"|#h,0H'D=&qmuϟ?`5Fצf$ /ٿfዹgW^cǎ  jժI"Ed޽u]'M4aϟ/KjlK(!?CF{_'ۇ~XHnY >իgM;tkΜ9,&ڵkG22|衇l'xBP1>}ښ{(&$M_5czeΝ6 2HϞ=m 6m$۷>gزeKɜ9}|މCtiǦZ-m Mԫjho&N(۷;vH=a^|yի1' M&}UT/>ҥKжt՝;w;wa2:tȶ1"ưa|q0Ąsg"& ʻķpÍv2n8-)ҥ-#v&[]v ir+U4p~zDQv"ԑϋ+? W]u}˗/ojq]b<ԩSbyf {PߐK'+{@m3$bNY+7nwn5IeOB~ժU䭷޲0UV-{Сxb0?R~x a̅-^}ՖbqoeܹZҸqco1p,>NR:s~:7F0a`$ƍ%ry:X!lt.L } dM6kfԻwoKLO t4fNXhٳո8HZ+̨ '&<#4>S[nf͚N')#˖-D6oA?:# (0x D?: D h64\hw<0hSQrAXnGڒWyXtd̘N{E4Al|{8V\AyLD&J ƫ$߭j=7C~{f͚e \}g+Db $b;}쑾⇿4dS1qeº~ڎԟ8XeSRЌɩYh"DfԨQ\ ̌HIӣ1=ھ4f$[К` xAX_>WC QuY{LC IBh 41M7']gW'D@ el|Th ̎И!պq!L3Q|%i33$T6vTNMj "3l0"$jﷃرcm8O:AG|fڵvq_H(KK!L׈FDKV B#ԩS9(GSȼ#5h>}z$>r03?7߄>7Up n6xF{HZV,L6;fO-}CpoM߈qǛ} iӼ9c3n1ѢsvĂ&M蓪/2RseN($^>li&boQ_`ey饗猁'SwG# )Q_; 8+p(z_P?;aF^ OA@->SVXaVZiɢ4f 6lF !N+H qfxFGKc(1žcsx#!}57PR*mrC0 Z@^ ~7;+䙻F&!+'5#GXm &URF &?Sd;"ou 2r~[b@wKٽ6B19808NnB$v\T!}C.1oċ7~lVG3@B#[%z{3 ~b2S<&NhDҍm}݉' If‘|쮜L &i'7߸1)+}GМw9\xqϹhGy׭o]^ɛ7զqa $tPN`щ\%,n҆ Gfg4fԇۻ )(p+aۃ%A "A(XR R DĊ"'H"2(īZ&) (ϓϿg]S3<ӽ~KC(t} _/H*{kA;PtMR%U E3e\\\zheF曏i.Tw1kku:i%-k7,׽Q'hPX{+SN Æ s:5ߑ?Ν;׿YԲ_Q%ǝw\y@ ޿uL"']%}*3r|};bG_j^Tw ye_]~OoT#:oy?5^ 6ͅ䯫\|eS *8'c1ٺf}up,f6lpF|ǡϼxX=T1 UhRSJP Ps+6<hhqzK+vwLo/|ԛKнzK(&t-' })pzꩧ"A_ }fbٛ@@t(HTH> uC}孓.&PRӟ}wE|-N;! K|}ν!dʢ$cg|IIP4ԣtmLVǮ T ::×^>9P~\sw./eFPN%TnUǐ7Hx͏Tǀ1I%-Utsҹ]TW߭FsFAAWjK:+t|+Puc*S?ahzzxIJc}ԯ_] ^ԗ&ǫ1P#P.:c{"QN+d_ޗ9NSNuEI"ősCg&sa 쩧@4RARaaos|`E꥓S`;ë3 ~% \]jMaPR{޼KXg[?,WU?Eǎ.бx]zjWЕ:AVPƶ~,h4!Q6j[me+U{j(II&^m}CO}Ѫݔ*0#Yt 4Ix%G=:Srokj>};)՗(y^藕ݢFAv}ǖOeSb~񶏭֫ͼ_^4i j/{-Տ CA)>^y6VϪ{5 G+-Ԗ׏%}et{9˭Z܃0Lso{o}Vt M<>aCUmmG/o/| (sm[gv1Ulg:)Kʳc2yKp~(;7Iv:x|Ş#{O:~V?fN+r~Ͻ:zm;PSyޭz&+OGT.\hu@욇:hJzN:|cS.Ot]ޑIonTQStI8~*3!cԫ!6})*i˥}cz6իN|0މh>ھcY'}afŽtX^=*:[=؈W氬1IUюt;Ԕc$GoYDʚ{OUd5 @:|Qd/E@&)V+  W/. +@@p ]  W/. +@@p ]  W/. +@@p ĽɳwߠpVZ! ޏ  %@@@ n_j D Eq@_ۗ! QQl KKKnrch޼M>4}*++mڴinC^#FXAA3lժU!1CHV 5j lС6k,_qɖ3, N߮z?~+3\fΜ={xxD@ )o>{]Ag}fݻwO`5\csεs=7R̙cu֍ vZ[~Ν;Ě6m*M4B[paB@rC O=E7o͆ o"Q;' p 3.nݺʕ+СC9Zc/<`%l֬ u=߾}{<ٴiJEEE(orm9+VK٨ H*:yCU –t񃮄PDPUQu\Ʒz'T2  |7oC#  :Re^d  G/ >*@@ цX  @:ҡ>@@ цX  @:ҡ>@@ цX  @:乺˃Q  z2L.  @ND3P@@ 3q&@@ 'r(  8   9 @Ȍ_f@ 'tҤTq*++$f_hذ]q6p@kݺkd gNOݻ  Ľs[TE]d/voy7/@GuڡC /g;3kŭ1[쪫r-..+W֘SQ@ ,[Ll2%ei3fժUnvۼy|SOM7Dv̙39@8R6sζ|r1bq#<裮_!Cs=g͛7;l.Sve{F>a(tӦMm̙]P }M0ʲ_4`Ϟ=֧OkҤ}. sa~#z0`h G^`!7x;| @ ) {৏E'aQOÔy{}70L R]jq @RhuOrz5\6Yx6 O?-YĶon={I&@ &++Vf͚_ aW^? @tG=~  K^|Ewn + !7߮ۻoȑQܴi幀7  @ RvG k v5z% q%6}4 Xwߵv嗛n!o _hÿ/n+)h K;?wsLbCuW5;6,U l=***[v  [yGn [[E((ҿx֬YNKv\gx; `7f=wm.0=Cþ|I2@"o-?}V^={lܸq l[@@`77ځR  /#d _n@@2"@f2A@rC/7ځR  {]JB@7|Se@@ M  P_4 O/|mJ@@*@@ |kSj T)@W% /  {D٠A˭s+bʼn@˖-O>~[|yfcǎֵkW{뭷l޽5Z#D y6}t;֭[7+lڴi$SL&MءC2#/lժUQ@D '`]6~x)!*U,̙3]gϞ @@R.t_ݺumڵ~zW;wZII5m45C};vwq8`۰aYvXДYAxaa>|ƍzQ@ $=QoflQDAr޽ys@ntsO@Jb5֒!dR /+Vvm3gNKaٳgG1c +//'FyK/|+***)? HzΟ0{vih"/ny.Pz0w+{T/P~@B e=vuVXaK."p .HZfM(* wV=#Gtujժse+@@& $ա]tO?jODzmٲ !.]D{K.6nHa@ $ 8>34hPDOx " uԱ|B=zp~[.еӽcԩSm۶6lذؗx H:]kժ aٲeNu1DF &D^Wu]yЏ+ ;TH0!! SХj`Pj@@՜  !@@j_ jl @@j_ jl Ľ_uC  \zv@HX/a26@@+@ܶ  @   \%G@ K @@ (9  _dl W/mG@@&c@@  :t`=r-ָܺΝ;Gebaȑ6|[n$ۂ .S o]OT@rD 4͛m׮]vUW͛裏2ʽn:kܸ+AQFzjoUw'Z4ce #@ (Jhذ͛7ou_-'/ncƌq.ZȕcԨQ(m#ʕ+Swv .?A+1c5iڴicY T5ki~5۠A[l^Zݻ /_NV c@+O۶mufgMW=}yyyvW5\cK/e<-[_W駟ٳV2F@ jW|6uذaV^Ԑ%#IDAT^=.ر_-ˬVZVTTdseee(y" @OW6k >裦 ە믻g *M4]d;묳sq @@f Ku5OCڵkMܺvj3^U|v=tgoᆨ"?>䓨uio̥Q@˵뮻W^gɵQ@ijxYC>Ma  @B &ś@@` j`7G@/@~cr@@rF/g   K19  9#jߚthR|:  (z\@HN/9?F@%@械  @r5  (@5E@ KΏ@@@ (,  @h;Yf |6k,۸qq%5UVVښ5kl;XEEEjv^@@ p FaӦMCgFٷo9sX.]l/2T7h ӧ͝;g]v>|8+!S@Ȯ@肿3gZAAٳ'k&Lpyo߾֯_oOi~5nfϞd+⋣ K͛g 4p<iذaԺT=ѰziV~}*,suv,# @ģ tU޽;bGja\]IOko;wZ۶mݲ۶mu5< >;j*;@𩽔4' 5K %o|.>|Vnݺ6nܸX.]Mc&ٕݻw^xN]{iɒ%v`^@@ IӕM6uCzƏ~*j7nFRUʘ1cܻG]V[=qD7$ 6؃>n@E ?]8p嗻+{{领s= C*V_<9TSNxbkѢE9za*{ǭQFGn! dU 0eel իF<裏RZO󯸸8va͛79L4/@@ Gsj!\ 4OX@rS 9Y-J  O/ @@ a  O/ @@ a  O/ @@ ĽՋT[=+)Wny䒍W&@@ (=  _B\@_ۏ#  %ś@@` (=  _B\@@?ݢW_YfƍseN?tkyY)SeeYƦOnUTTdd Mo>}hs̱.]ɓ.|-c=f_y/AY>}lܹ֯_?/ " @BM0in߾֯_oOkٚ@q{7ǫH@@<‡@@!@v  @JRN@@`(%  |ԫW/%;NRy_TԽsΩ @@"E(X@@/@6  @D/B  ~15D@" @@ !   P _ T7|c͚56nܘ˥!:vhf͚W 5D 4߾}C6p@3gu&Of̥FaӦMCge@! &Ll~z{'lԩz7e.ū̙3@j@?v-[z;o߿;;ӵ,X` pN9ȑ# ڹ瞛\*pM4B;|7  P .M޽{e2mݺe׫WHuԱ>}^ ,RY5j9z@@f $k3fXyyM81. [lqٴn:*6m+.Or,*#! I~ѣG[~~=#VRR)% ^.}颅yY Fm6l.Or,@@# //lk֬Iqu?iRΝ;m۶nYm۶ͺvy\*K~@@f$jnVpgQQQ֭[ qTZtidN޽{mi$ʒ6pv H*S؃>`x-nݺWWӺunh8:)ʒz C O=n5r5m'˨ns.N:ŋE-2˥V"v@@  *c[d*}G)JW Β-KΝ#l@B>_~N~*O.@j@肿ݜ@^z^E@B%@2  @*  *P5'A@ އW@@P Ľ_jHe@@"E(X@@/@6  @D/B  ~15D@" @@ !  ? 4IENDB`iminuit-2.24.0/doc/_static/mov2gif.sh0000755000000000000000000000006614332717401014312 0ustar00#!/bin/sh ffmpeg -i $1 -pix_fmt rgb8 -r 10 output.gif iminuit-2.24.0/doc/_static/numba_vs_scipy.svg0000644000000000000000000012322314332717401016145 0ustar00 2023-05-09T16:51:10.803151 image/svg+xml Matplotlib v3.7.1, https://matplotlib.org/ iminuit-2.24.0/doc/_static/overhead.svg0000644000000000000000000012063014332717401014720 0ustar00 2023-05-09T16:51:11.591610 image/svg+xml Matplotlib v3.7.1, https://matplotlib.org/ iminuit-2.24.0/doc/_static/roofit_vs_iminuit+numba.svg0000644000000000000000000015202714332717401017776 0ustar00 2023-05-09T16:51:12.564398 image/svg+xml Matplotlib v3.7.1, https://matplotlib.org/ iminuit-2.24.0/doc/about.rst0000644000000000000000000001007014332717401012614 0ustar00.. include:: bibliography.txt .. _about: About ===== What is iminuit? ---------------- iminuit is the fast interactive IPython-friendly minimiser based on `Minuit2`_ C++ library maintained by CERN's ROOT team. The corresponding version of the C++ code is listed after the '+' in the iminuit version string: |release|. For a hands-on introduction, see the :ref:`tutorials`. **Easy to install with binary wheels for CPython and PyPy** If you use one of the currently maintained CPython versions on Windows, OSX, or Linux, installing iminuit from PyPI is a breeze. We offer pre-compiled binary wheels for these platforms. We even support the latest PyPy. On other platforms you can also install the iminuit source package if you have a C++ compiler that supports C++14 or newer. Installing the source package compiles iminuit for your machine automatically. All required dependencies are installed from PyPI automatically. **Robust optimiser and error estimator** iminuit uses Minuit2 to minimise your functions, a battle-hardened code developed and maintained by scientists at CERN, the world's leading particle accelerator laboratory. Minuit2 has good performance compared to other minimizers, and it is one of the few codes out there which compute error estimates for your parameters. When you do statistics seriously, this is a must-have. **Support for NumPy** iminuit interoperates with NumPy. You can minimize functions that accept parameters as positional arguments, but also functions that accept all parameters as a single NumPy array. Fit results are either NumPy arrays or array-like types, which mimic key aspects of the array interface and which are easily convertible to arrays. **Interactive convenience** iminuit extracts the parameter names from your function signature (or the docstring) and allows you access them by their name. For example, if your function is defined as ``func(alpha, beta)``, iminuit understands that your first parameter is ``alpha`` and the second ``beta`` and will use these names in status printouts (you can override this inspection if you like). It also produces pretty messages on the console and in Jupyter notebooks. **Support for Cython, Numba, JAX, Tensorflow, ...** iminuit was designed to work with functions implemented in Python and with compiled functions, produced by Cython, Numba, etc. **Successor of PyMinuit** iminuit is mostly compatible with PyMinuit. Existing PyMinuit code can be ported to iminuit by just changing the import statement. If you are interested in fitting a curve or distribution, take a look at `probfit`_. Who is using iminuit? --------------------- This is a list of known users of iminuit. Please let us know if you use iminuit, we like to keep in touch. * probfit_ * gammapy_ * flavio_ * Veusz_ * TensorProb_ * threeML_ * pyhf_ * zfit_ * ctapipe_ * lauztat_ Technical docs -------------- When you use iminuit/Minuit2 seriously, it is a good idea to understand a bit how it works and what possible limitations are in your case. The following links help you to understand the numerical approach behind Minuit2. The links are ordered by recommended reading order. * The `MINUIT paper`_ by Fred James and Matts Roos, 1975. * Wikipedia articles for the `Quasi Newton Method`_ and `DFP formula`_ used by MIGRAD. * `Variable Metric Method for Minimization`_ by William Davidon, 1991. * Original user guide for C++ Minuit2: :download:`MINUIT User's guide ` by Fred James, 2004. Team ---- iminuit was created by **Piti Ongmongkolkul**. It is a logical successor of pyminuit/pyminuit2, created by **Jim Pivarski**. It is now maintained by **Hans Dembinski** and the Scikit-HEP_ community. Maintainers ~~~~~~~~~~~ * Hans Dembinski (@HDembinski) [current] * Christoph Deil (@cdeil) * Piti Ongmongkolkul (@piti118) * Chih-hsiang Cheng (@gitcheng) Contributors ~~~~~~~~~~~~ * Jim Pivarski (@jpivarski) * Henry Schreiner (@henryiii) * David Men\'endez Hurtado (@Dapid) * Chris Burr (@chrisburr) * Andrew ZP Smith (@energynumbers) * Fabian Rost (@fabianrost84) * Alex Pearce (@alexpearce) * Lukas Geiger (@lgeiger) * Omar Zapata (@omazapa) iminuit-2.24.0/doc/benchmark.rst0000644000000000000000000002370414332717401013444 0ustar00.. include:: bibliography.txt Benchmarks ========== Speed of an unbinned fit ------------------------ ``iminuit`` comes with a couple of built-in cost functions, but also makes it easy for users to implement their own. One motivation to use your own cost function is to get the best possible performance. If your fit takes more than a minute (possible if you do an unbinned fit on a large sample or if you have a model with many parameters), you may wonder whether there are wells to speed things up. There are, read on. There are a few simple steps which increase performance without much effort. - If you have a large sample, use a binned fit instead of an unbinned fit, e.g. :class:`iminuit.cost.BinnedNLL` instead of :class:`iminuit.cost.UnbinnedNLL`. This can easily give you a factor 100 speed-up. - Use faster implementations of probability densities from `numba_stats`_. - Use `numba`_ to compile the cost function; try options `parallel=True` and `fastmath=True` to parallelize the computation. This works best if you use the distributions from `numba_stats`_. Next we show some benchmarks to prove some of these points. The gain of using a binned fit is demonstrated elsewhere, see "Binned vs. unbinned fits" in :ref:`studies`. For the following benchmarks, we consider a common model in high-energy physics which describes a mixture of two components, a background described by a truncated exponential and a signal described by a normal distribution. The model is fitted to a dataset of varying size using the maximum-likelihood method. Our cost function is the negative sum of log-probabilities. .. code-block:: python import numpy as np from numba_stats import norm, truncexpon x = ... # data def model(x, z, mu, sigma, slope): # background is truncated to the observation window [0, 1] b = truncexpon.pdf(x, 0, 1, 0, slope) s = norm.pdf(x, mu, sigma) return (1 - z) * b + z * s # handwritten negative log-likelihood def nll(z, mu, sigma, slope): return -np.sum(np.log(model(x, z, mu, sigma, slope))) It is also possible to write down a ``log_model`` computed from the ``logpdf`` functions which are available for the two component distributions, but the code is more complex and the gain is small, so it is not explicitly shown here. The following plot shows that this cost function computed with pdfs from `numba_stats`_ is much faster than using those from `scipy`_. .. image:: _static/numba_vs_scipy.svg It is an order of magnitude for small samples and still a factor of three for very large samples. Instead of using a handwritten negative log-likelihood, it is convenient to use the built-in class :class:`iminuit.cost.UnbinnedNLL`. This increases the call overhead slightly compared to a handwritten function compiled with `numba`_. The smallest possible overhead is obtained by compiling the function with a ``numba.cfunc`` interface, which ``iminuit`` supports as a special case. .. code-block:: python import numba as nb import numpy as np from numba_stats import norm, truncexpon # JIT'ed handwritten negative log-likelihood @nb.njit def nll(z, mu, sigma, slope): return -np.sum(np.log(model(x, z, mu, sigma, slope))) # as before, but with a cfunc interface @nb.cfunc(nb.double(nb.uintc, nb.types.CPointer(nb.double))) def cost(n, par): z, mu, sigma, slope = nb.carray(par, (n,)) return -np.sum(np.log(model(x, z, mu, sigma, slope))) The three ways of calling the function are compared in the following plot. The call overhead is only relevant for very small samples, which rarely occur in practice and if they do, speed is nothing to worry about. Thus, it is safe to use :class:`iminuit.cost.UnbinnedNLL`. .. image:: _static/overhead.svg A very powerful feature of `numba`_ is auto-parallelization. The distributions in `numba_stats`_ are written so that they benefit from auto-parallelization. To enable this, we only have to change the decorator. .. code-block:: python import numba as nb import numpy as np from numba_stats import norm, truncexpon # JIT'ed handwritten negative log-likelihood, parallelized @nb.njit(parallel=True, fastmath=True) def nll(z, mu, sigma, slope): return -np.sum(np.log(model(x, z, mu, sigma, slope))) Always combine ``parallel=True`` with ``fastmath=True``, because they synergize. Auto-parallelization speeds up the calculation of the cost function over large datasets considerably, but slows it down over small datasets. Only enable it when you profit from it. The following plot shows the impact of auto-parallelization. We compare the performance of a fit of our `numba`_ JIT'ed cost function with iminuit to an equivalent fit with the `RooFit`_ framework (from ROOT-v6.28/00 installed with ``conda``). The latter can be sped up by enabling ``BatchMode``, which is roughly comparable to ``fastmath=True``, and ``NumCPU`` which roughly corresponds to ``parallel=True``. .. image:: _static/roofit_vs_iminuit+numba.svg ``iminuit`` in combination with a `numba`_ JIT'ed cost function outperforms `RooFit`_ by a large margin, except when the dataset is so small that performance is not relevant anyway. The gap is particularly large when parallel computation is enabled. Performance hints ^^^^^^^^^^^^^^^^^ - Use the distributions from `numba_stats`_ instead of the `scipy`_ equivalents to get a large performance gain. Use auto-parallelization to speed up the computation of the cost function for large datasets. - ``iminuit`` with a `numba_stats`_ distributions and a `numba`_ compiled cost function is much faster than `RooFit`_. The gain is especially large when parallel computation is enabled. - :class:`iminuit.cost.UnbinnedNLL` is as fast as the fasted possible function you can write by hand for large datasets. With extra manual work, you can gain a bit for small datasets, but then the extra gain in performance hardly matters. Minuit2 vs other optimisers --------------------------- We compare the performance of Minuit2 (the code that is wrapped by iminuit) with other minimizers available in Python. We compare Minuit with the strategy settings 0 to 2 with several algorithms implemented in the `nlopt`_ library and `scipy.optimize`_. . All algorithms minimize a dummy cost function .. code-block:: python def cost_function(par): z = (y - par) return sum(z ** 2 + 0.1 * z ** 4) where ``y`` are samples from a normal distribution scaled by a factor of 5. The second term in the sum assures that the cost function is non-linear in the parameters and not too easy to minimize. No analytical gradient is provided, since this is the most common way how minimizers are used for small problems. The cost function is minimized for a variable number of parameters from 1 to 100. The number of function calls is recorded and the largest absolute deviation of the solution from the truth. The fit is repeated 100 times for each configuration to reduce the scatter of the results, and the medians of these trails are computed. The scipy algorithms are run with default settings. For nlopt, a stopping criterion must be selected. We stop when the absolute variation in the parameters is becomes less than 1e-3. The results are shown in the following three plots. The best algorithms require the fewest function calls to achieve the highest accuracy. .. image:: _static/bench.svg .. image:: _static/bench2d.svg Shown in the first plot is the number of calls to the cost function divided by the number of parameters. Smaller is better. Note that the algorithms achieve varying levels of accuracy, therefore this plot alone cannot show which algorithm is best. Shown in the second plot is the accuracy of the solution when the minimizer is stopped. The stopping criteria vary from algorithm to algorithm. The third plot combines both and shows accuracy vs. number of function calls per parameter for fits with 2, 10, and 100 parameters, as indicated by the marker size. Since higher accuracy can be achieved with more function evaluations, the most efficient algorithms follow diagonal lines from the top left to the bottom right in the lower left edge of the plot. Discussion ^^^^^^^^^^ The following discussion should be taken with a grain of salt, since experiments have shown that the results depend on the minimisation problem. Also not tested here is the robustness of these algorithms when the cost function is more complicated or not perfectly analytical. * Minuit2 is average in terms of accuracy vs. efficiency. Strategy 0 is pretty efficient for fits with less than 10 parameters. The typical accuracy achieved in this problem is about 0.1 to 1 %. Experiments with other cost functions have shown that the accuracy strongly depends on how parabolic the function is near the minimum. Minuit2 seems to stop earlier when the function is not parabolic, achieving lower accuracy. * The scipy methods Powell and CG are the most efficient algorithms on this problem. Both are more accurate than Minuit2 and CG uses much fewer function evaluations, especially in fits with many parameters. Powell uses a similar amount of function calls as Minuit2, but achieves accuracy at the level of 1e-12, while Minuit2 achieves 1e-3 to 1e-6. * An algorithm with a constant curve in the first plot has a computation time which scales linearly in the number of parameters. This is the case for the Powell and CG methods, but Minuit2 and others that compute an approximation to the Hesse matrix scale quadratically. * The Nelder-Mead algorithm shows very bad performance with weird features. It should not be used. On the other hand, the SBPLX algorithm does fairly well although it is a variant of the same idea. In summary, Minuit2 (and therefore iminuit) is a good allrounder, but it is not outstanding in terms of convergence rate or accuracy. Using strategy 0 seem safe to use: it speeds up the convergence without reducing the accuracy of the result. If fast convergence is critical, it can be useful to try others minimisers. The scipy minimisers are accessible through :meth:`iminuit.Minuit.scipy`. iminuit-2.24.0/doc/bibliography.txt0000644000000000000000000000307314332717401014171 0ustar00.. _RooFit: https://root.cern.ch/master/namespaceRooFit.html .. _scipy: https://www.scipy.org/ .. _scipy.optimize: https://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html .. _numba: https://numba.pydata.org .. _numba_stats: https://github.com/HDembinski/numba-stats .. _jax: https://jax.readthedocs.io .. _nlopt: https://nlopt.readthedocs.io/en/latest .. _pyhf: https://github.com/diana-hep/pyhf .. _ctapipe: https://github.com/cta-observatory/ctapipe .. _lauztat: https://github.com/marinang/lauztat .. _threeML: https://github.com/threeML/threeML .. _Veusz: https://github.com/veusz/veusz .. _TensorProb: https://github.com/tensorprob/tensorprob .. _Scikit-HEP: http://scikit-hep.org .. _probfit: https://github.com/scikit-hep/probfit .. _gammapy: https://github.com/gammapy/gammapy .. _flavio: https://github.com/flav-io/flavio .. _zfit: https://github.com/zfit/zfit .. _Minuit2: https://root.cern.ch/guides/minuit2-manual .. _PyMinuit: http://code.google.com/p/pyminuit .. _ipythonnb: http://ipython.org/ipython-doc/dev/notebook/index.html .. _Quasi Newton Method: http://en.wikipedia.org/wiki/Quasi-Newton_method .. _DFP formula: http://en.wikipedia.org/wiki/Davidon-Fletcher-Powell_formula .. _Variable Metric Method for Minimization: https://www.osti.gov/biblio/4252678 .. _A New Approach to Variable Metric Algorithm: http://comjnl.oxfordjournals.org/content/13/3/317.full.pdf+html .. _MINUIT paper: https://doi.org/10.1016/0010-4655(75)90039-9 .. _setuptools: https://pypi.python.org/pypi/setuptools .. _pytest: http://pytest.org .. _zenodo: https://zenodo.org/record/4310361 iminuit-2.24.0/doc/changelog.rst0000644000000000000000000013471614332717401013447 0ustar00.. include:: bibliography.txt .. _changelog: Changelog ========= 2.24.0 (August 15, 2023) ------------------------ - Iteration limit in smart sampling to fix behavior for step functions (`#928 `_) - Clarify meaning of 2d contours in minuit.draw_mnmatrix (`#927 `_) - Support interval type and check compatibility with pydantic (`#922 `_) 2.23.0 (July 28, 2023) ---------------------- - Fix ``CostSum.visualize`` bug (`#918 `_) - Fix ``_safe_log`` on systems which use 32 bit floats (`#915 `_), (`#919 `_) - Skip test_interactive and friends when ipywidgets is not installed (`#917 `_) - Turn negative zero into positive zero in ``pdg_format`` (`#916 `_) - Remove warning in ``test_cost.py`` by using ``pytest.warns`` instead of ``pytest.raises`` (`#914 `_) 2.22.0 (June 22, 2023) ---------------------- - Hide confusing notes in docs: "not to be initialized by users." (`#906 `_) - Fix: sdist size reduction (`#904 `_) - Fix tests on freebsd, remove util._jacobi (`#903 `_) - Update conclusions after the fix from jonas (`#899 `_) - Use scikit-build-core (`#812 `_) - Fix: make nograd not use grad at all in automatic diff doc (`#895 `_) - Bump pypa/cibuildwheel from 2.12.3 to 2.13.0 (`#897 `_) - Add minuit.fixto (`#894 `_) - New benchmarks (`#893 `_) - Make covariance fields in display easier to understand (`#891 `_) - Improve docs (`#890 `_) - Bump pypa/cibuildwheel from 2.12.1 to 2.12.3 (`#886 `_) - Update fcn.hpp (`#889 `_) - Fix-typo-in-basic (`#888 `_) - Add leastsquares.pulls and leastsquares.prediction (`#880 `_) - Better log-spacing detection (`#878 `_) - Roofit tutorials (`#877 `_) - Rename keyword nbins to bins in unbinnedcost.visualize (`#876 `_) - Ignore missing matplotlib when calling minuit._repr_html_() (`#875 `_) - Forward kwargs in minuit.visualize to plotting function (`#874 `_) - Add hide_modules and deprecated_parameters (`#873 `_) - Update progressbar (`#872 `_) - Better ruff settings and adjustments, improvements to readme (`#871 `_) - Use unicodeitplus instead of unicodeit to render latex as unicode (`#868 `_) - Add roofit tutorial (`#867 `_) - Improve error message for cost function (`#863 `_) - Experimental mncontour algorithm (`#861 `_) - Integer as variable (`#860 `_) - Replace flake8 with ruff (`#859 `_) - Add basic latex display support if unicodeit is installed (`#858 `_) 2.21.3 (April 03, 2023) ----------------------- - Fix template input modification bug in template class (`#856 `_) - Better docs for limits from annotated model parameters (`#853 `_) 2.21.2 (March 19, 2023) ----------------------- - Fix CITATION.CFF 2.21.1 (March 18, 2023) ----------------------- - Fix string annotations (`#849 `_) - Specifiy minimum required numpy version (`#848 `_) 2.21.0 (March 03, 2023) ----------------------- - Fix of matrix_format (`#843 `_) - Support annotated model parameters (`#839 `_) - Visualize fit in minuit._repr_html_ (`#838 `_) 2.20.0 (February 13, 2023) -------------------------- - Fix coverage, typing, template for 2d models (`#836 `_) - Template mix (`#835 `_) - Docs: use 'req@url' syntax to install from remote vcs (`#834 `_) 2.19.0 (February 10, 2023) -------------------------- - Remove setup.cfg (`#833 `_) - Fix typo "probility" to "probability" in minos doc (`#832 `_) - Prediction for all binned likelihoods (`#831 `_) - Template prediction (`#820 `_) - Upgrade old build and ci (`#817 `_) - Add configurable number of bins for extended/unbinnednll (`#815 `_) 2.18.0 (December 14, 2022) -------------------------- - Add more checks for gradients (`#810 `_) - Bump pypa/cibuildwheel from 2.10.2 to 2.11.2 (`#808 `_) - Added visualize function to minuit (`#799 `_) - Move tutorials (`#806 `_) 2.17.0 (September 26, 2022) --------------------------- - Add python 3.11, drop 3.6 (`#792 `_) - Add Template fitting method from Argüelles, Schneider, Yuan (`#789 `_) - Deprecate `iminuit.cost.BarlowBeestonLite` in favor of `iminuit.cost.Template` 2.16.0 (August 16, 2022) ------------------------ - Root update (`#786 `_) - Fix corner case treatment of linear constraint (`#785 `_) - Comparison with broadcasting (`#784 `_) - Fix typing issues and enable mypy in pre-commit (`#783 `_) - Make fixedview act as mask for other views (`#781 `_) 2.15.2 (August 03, 2022) ------------------------ - Improve docs for minimize (`#777 `_) - Fix for minuit.interactive when using an array-based function (`#776 `_) 2.15.1 (July 28, 2022) ---------------------- - Fix pickling of all builtin cost functions (`#773 `_) 2.15.0 (July 27, 2022) ---------------------- - Smart sampling (`#769 `_) - Enhance interactive (`#768 `_) 2.14.0 (July 25, 2022) ---------------------- - Interactive fitting (`#766 `_) 2.13.0 (July 17, 2022) ---------------------- - Interpolated mncontour (`#764 `_) - Added mnmatrix plot (`#763 `_) - Close mncontour for convenience (`#761 `_) - Update tutorials (`#760 `_) 2.12.2 (July 15, 2022) ---------------------- - fix a bug in error heuristic when parameters have negative values and prevent assigning negative values to errors (`#759 `_) 2.12.1 (July 1, 2022) --------------------- Fixes ~~~~~ - ``cost.BarlowBeestonLite``: method "hpd" has been modified to fix performance in cases where bins are not dominated by a single template 2.12.0 (June 21, 2022) ---------------------- New features ~~~~~~~~~~~~ - New cost function ``cost.BarlowBeestonLite`` for template fits with correct uncertainty propagation for templates obtained from simulation or sWeighted data, written together with @AhmedAbdelmotteleb - Formerly private chi2 utility cost functions (``cost.poisson_chi2``, etc.), are now part of public API - Support custom grid in ``Minuit.profile``, ``iminuit.mncontour``, ``iminuit.contour`` - Handle common CL values in ``Minuit.mnprofile`` and ``Minuit.mncontour`` without scipy Fixes ~~~~~ - Skip tests that use ``np.float128`` on platforms where this type is not supported - ``Minuit.valid`` now returns ``False`` if EDM is NaN - ``subtract_min`` setting is no longer ignored by ``Minuit.draw_contour`` Documentation ~~~~~~~~~~~~~ - New study about template fits Other ~~~~~ - ``Minuit`` no longer warns when a function is used that has no ``errordef`` attribute and ``Minuit.errordef`` is not explicitly set. The function is assumed to be chi-square-like up to an arbitrary constant, unless ``errordef`` is explicitly set to something else. - More type correctness in API, better hiding of private objects in library - Option to use external pybind11, by @henryiii - Update to pybind11-v2.9.2, by @henryiii - Update to root-v6-25-02 plus our patches - Several minor cleanups and internal tool updates, by @henryiii 2.11.2 (March 28, 2022) ----------------------- Other ~~~~~ - Fixed wording in cost function tutorial 2.11.1 (March 27, 2022) ----------------------- Fixes ~~~~~ - Fixed a failure of ``util.make_with_signature`` in some situations Other ~~~~~ - Raise numpy.VisibleDeprecationWarning instead of warnings.DeprecationWarning - ``util.propagate`` is deprecated in favour of ``jacobi.propagate`` from the jacobi library 2.11.0 (March 27, 2022) ----------------------- New features ~~~~~~~~~~~~ - All builtin cost functions now support multidimensional data - ``Matrix.to_dict`` was added for symmetry with ``BasicValueView.to_dict`` - For long-running fits, total runtime is now shown in ``FMin`` display and total runtime can be accessed via property ``FMin.time`` Fixes ~~~~~ - In binned fits when ``ndof`` is zero, show ``reduced chi2 = nan`` in the ``FMin`` display instead of raising a ZeroDivisionError Documentation ~~~~~~~~~~~~~ - Tutorials and studies are now listed separately - Tutorial for fits of multivariate data were added - The cost function tutorial was improved - Studies in regard to performance were added, including a comparison with RooFit 2.10.0 (March 4, 2022) ---------------------- New features ~~~~~~~~~~~~ - ``UnbinnedNLL`` and ``ExtendedUnbinnedNLL`` now support models that predict the ``logpdf`` instead of the ``pdf`` when the extra keyword ``log=True`` is set; when it is possible, using the ``logpdf`` instead of the ``pdf`` for fitting is faster and numerically more stable 2.9.0 (January 7, 2022) ----------------------- Fixes ~~~~~ - ``Minuit.draw_mncontour`` now works with matplotlib >= 3.5 - Builtin cost functions now work correctly when the mask is set and data is updated on the existing cost function Performance ~~~~~~~~~~~ - Builtin cost functions are now more performant when used with weighted binned data Other ~~~~~ - Wheels for Python 3.10, by @henryiii - Bump pybind11 to 2.9.0, by @henryiii 2.8.4 (October 11, 2021) ------------------------ Fixes ~~~~~ - Pickling of ``util.Matrix`` resulted in incomplete state after unpickling, which would cause an exception when you tried to print the matrix Documentation ~~~~~~~~~~~~~ - New tutorial on fitting PDFs that depend on a conditional variable - Fixed JAX tutorial, adapting to change in their interface - Extended documentation of cost functions 2.8.3 (September 3, 2021) ------------------------- New features ~~~~~~~~~~~~ - ``util.propagate`` now discriminates between diverging derivates (using the value NaN for the derivate) and non-converging derivatives (using the best value computed so far for the derivative) Documentation ~~~~~~~~~~~~~ - Fixes for faulty LaTeX rendering in some tutorials Other ~~~~~ - Support cross-compiling of ARM on Conda, by @henryiii 2.8.2 (August 15, 2021) ----------------------- Fixes ~~~~~ - ``Minuit.draw_mncontour`` can now be used by passing a single float to keyword ``cl``, in addition to passing a list of floats - Use ``pybind11::ssize_t`` everywhere instead of non-standard ``ssize_t`` to fix compilation against Python-3.10 on Windows, co-authored with @cgohlke Documentation ~~~~~~~~~~~~~ - Docstring improved for ``Minuit.mncontour``, advice added on how to draw closed curve with points returned by ``Minuit.mncontour`` - Docstring improved for ``Minuit.draw_mncontour``, parameters and returned objects are now properly documented 2.8.1 (August 4, 2021) ---------------------- Other ~~~~~ - @henryiii added Apple Silicon wheels - @odidev added Linux aarch64 wheels 2.8.0 (July 25, 2021) --------------------- Minor API change ~~~~~~~~~~~~~~~~ - ``Minuit.mncontour`` now raises ``RuntimeError`` instead of ``ValueError`` if it is not called at a valid minimum point New features ~~~~~~~~~~~~ - ``Minuit.mncontour`` can now be called at any point without running a minimiser before, similar to ``Minuit.minos`` Fixes ~~~~~ - ``Minuit.mncontour`` used to fail if called twice in a row 2.7.0 (July 4, 2021) -------------------- Minor API change ~~~~~~~~~~~~~~~~ - If ``Minuit.hesse`` is called when ``Minuit.fmin`` is ``None``, an instance ``Minuit.fmin`` is now created. If Hesse fails, the code does not raise an exception anymore, since now the error state can be accessed as usual from the ``Minuit.fmin`` object. Users who relied on the exception should check ``Minuit.fmin`` from now on. New features ~~~~~~~~~~~~ - ``Minuit.scipy`` can be used to minimise with SciPy algorithms. These may succeed when ``Minuit.migrad`` fails and support additional features. Some algorithms allow one to pass a function that returns the Hessian matrix (which may be computed analytically or via automatic differentiation provided by other libraries like JAX). Other algorithms support minimisation under arbitrary non-linear constraints. - ``util.FMin`` has new html/text representations; the field ``Valid parameters`` was removed, a title with the name of the minimisation method was added - ``Minuit.tol`` now accepts the values 0 and ``None``, the latter resets the default - Builtin cost functions now support models that return arrays in long double precision ``float128``. In this case, all computations inside the cost function are also done in higher precision. - Builtin cost functions now raise a warning if the user-defined model does not return a numpy array Fixes ~~~~~ - Calling ``Minuit.migrad`` with a call limit under some circumstances used much more calls than expected and did not report that the call limit was reached (patch submitted to ROOT) - ``Minuit.hesse`` no longer sets the status of the FunctionMinimum unconditionally to valid if it was invalid before - Repeated calls to ``Minuit.hesse`` no longer accumulate calls and eventually exhaust the call limit, the call counter is now properly reset - Calling ``Minuit.minos`` repeatedly now does not recompute the Hessian and avoids a bug that used to exhaust the call limit before in this case Documentation ~~~~~~~~~~~~~ - Tutorial notebooks are now fully integrated into the HTML documentation - A tutorial on using constrained minimisation from SciPy for HEP task was added Other ~~~~~ - ``util.BasicView`` is now a proper abstract base class 2.6.1 (May 13, 2021) -------------------- Fixes ~~~~~ - Calling ``Minuit.fixed[...] = False`` on parameter that was not fixed before lead to undefined behaviour in Minuit2 C++ code (patch submitted to ROOT) Other ~~~~~ - Upgrade Minuit2 C++ code to latest ROOT master with simplified internal class structure and class tags replaced with enums 2.6.0 (May 2, 2021) ------------------- New features ~~~~~~~~~~~~ - Builtin cost functions now report the number of data points with the attribute ``Cost.ndata`` - New attribute ``Minuit.ndof`` returns the degrees of freedom if the cost function reports it or NaN - New attribute ``FMin.reduced_chi2`` to report the reduced chi2 of the fit; returns NaN if the reduced chi2 cannot be computed for the cost function, in case of unbinned maximum-likelihood or when the attribute ``Cost.ndata`` is missing 2.5.0 (April 30, 2021) ---------------------- New features ~~~~~~~~~~~~ - ``util.merge_signatures`` added based on ``merge_user_func`` from ``probfit``, by @mbaak - ``util.make_with_signature`` added to create new functions with renamed arguments - ``util.BasicView.to_dict`` added, by @watsonjj - ``util.BasicView`` and ``util.Matrix`` now supports element selection with sequences like ``numpy.ndarray`` - ``util.propagate`` to error propagate covariance matrices from one vector space to another (Jacobi matrix is computed numerically) Fixes ~~~~~ - ``util.BasicView`` now supports slices of the form ``a[:len(a)]`` or ``a[:M]`` with ``M > len(a)`` like other Python containers - ``util.Matrix`` now returns a square matrix when it is used with a slice or item selection - Missing comma in BibTeX entry shown in CITATION.rst, by Ludwig Neste Other ~~~~~ - ``util.describe`` returns list instead of tuple Documentation ~~~~~~~~~~~~~ - Better docstring for ``util.FMin`` - New tutorial on how to do simultaneous fits / adding likelihoods, by @watsonjj - New tutorial on how to use builtin cost function - New tutorial about how to draw error bands around fitted curves 2.4.0 (February 10, 2021) ------------------------- New features ~~~~~~~~~~~~ - ``minimize`` - Keyword ``method`` now accepts "migrad" and "simplex" - Keyword ``option`` now supports keyword "stra" to set ``Minuit.strategy`` - ``OptimizeResult.message`` now states if errors are not reliable - ``Minuit`` now supports functions wrapped with ``functools.partial``, by @jnsdrtlf Other ~~~~~ - Upgrade Minuit2 C++ code in ROOT to latest version with following improvements - improvement of seed when using an analytical gradient - fix of last minimum state added twice to vector of minimum states in some cases (no impact for iminuit users, but saves a bit of memory) - Documentation improvements - Updated tutorial about automatic differentiation, added comparison of ``numba.njit`` and ``jax.jit`` 2.3.0 (January 24, 2021) ------------------------ New features ~~~~~~~~~~~~ - ``cost.BinnedNLL`` and ``cost.ExtendedBinnedNLL`` now support weighted binned data Bug-fixes ~~~~~~~~~ - ``FMin.edm_goal`` now remains unchanged if ``Minuit.hesse`` is run after ``Minuit.migrad`` Other ~~~~~ - Update to cibuildwheels-1.8.0 and workflow simplification, by @henryiii 2.2.1 (December 22, 2020) ------------------------- Minor improvements ~~~~~~~~~~~~~~~~~~ - ``Minuit.profile``, ``Minuit.mnprofile``, ``Minuit.contour``, ``Minuit.draw_profile``, ``Minuit.draw_mnprofile``, and ``Minuit.draw_contour`` can now be called with ``subtract_min=True`` even if ``Minuit.fmin`` is None - ``__version__`` now also displays the ROOT version of the C++ Minuit2 library - Support for adding constant numbers to cost functions, this allows you to write ``sum(cost1, cost2, ...)`` and may be useful to subtract a constant bias from the cost Other ~~~~~ - Documentation improvements - Further transition to numpydoc - Clarified that iminuit is based on ROOT code - List full iminuit version including ROOT version in docs - Added type hints to many interfaces (incomplete) - Renamed ``_minuit`` to ``minuit``, making the module public - Renamed ``_minimize`` to ``minimize``, making the module public - pydocstyle added to pre-commit checks 2.2.0 (December 20, 2020) ------------------------- New features ~~~~~~~~~~~~ - Cost functions in ``cost`` are now additive, creating a new cost function with the union of parameters that returns the sum of the results of the individual cost functions - ``cost.NormalConstraint`` was added as a means to add soft constraints on a parameter, can also be used to set up a covariance matrix between several parameters Other ~~~~~ - Documentation improvements, started transition to numpydoc 2.1.0 (December 18, 2020) ------------------------- New features ~~~~~~~~~~~~ - Minuit object is now pickle-able and copy-able - More efficient internal conversion between Python objects and ``std::vector`` - ``Minuit.minos`` can now be called without calling ``Minuit.migrad`` first, which allows one to use an external minimiser to find a minimum and then compute Minos errors for it Bug-fixes ~~~~~~~~~ - User-supplied gradient functions that return a ``torch.Tensor`` now work again - Matrix display now shows numbers correctly even if entries differ in magnitude Other ~~~~~ - Unit tests are included again in sdist package - ``Minuit.grad`` now returns ``numpy.ndarray`` instead of a ``list`` - Fixes for ``conda`` builds on Windows platform with ``msvc``, by @henryiii - Updated and unified documentation on how to cite iminuit - ``print()`` applied to ``Minuit.params``, ``Minuit.merrors``, ``Minuit.covariance``, ``Minuit.fmin`` now returns the pretty text version again instead of the ``repr`` version - Update to pybind11 v2.6.1 2.0.0 (December 7, 2020) ------------------------ This is a breaking change for Interface that was deprecated in 1.x has been removed. In addition, breaking changes were made to the interface to arrive at a clean minimal state that is easier to learn, safer to use, and ready for the long-term future. **To keep existing scripts running, pin your major iminuit version to <2**, i.e. ``pip install 'iminuit<2'`` installs the 1.x series. Under the hood, Cython was replaced with pybind11 to generate the bindings to the C++ Minuit2 library. This simplified the code considerably (Cython is bad at generating correct C++ bindings, while it is a breeze with pybind11). Removed and changed interface (breaking changes) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ``Minuit.__init__`` - Keywords ``error_*``, ``fix_*``, and ``limit_*`` were removed; assign to ``Minuit.errors``, ``Minuit.fixed``, and ``Minuit.limits`` to set initial step sizes, fix parameters, and set limits - Keyword ``pedantic`` was removed; parameters must be initialised with values now or an error is raised - Keyword ``errordef`` was removed; assign to ``Minuit.errordef`` to set the error definition of the cost function or better create an attribute called ``errordef`` on the cost function, Minuit uses this attribute if it exists - Keyword ``throw_nan`` was removed; assign to ``Minuit.throw_nan`` instead - Keyword ``print_level`` was removed; assign to ``Minuit.print_level`` instead - Setting starting values with positional parameters is now allowed, e.g. ``Minuit(my_fcn, 1, 2)`` initialises the first parameters to 1 and the second to 2 - Keyword ``use_array_call`` was removed; call type is inferred from the initialisation value, if it is a sequence, the array call is used (see next item below) - ``Minuit.from_array_func`` was removed; use ``Minuit(some_numpy_function, starting_array)`` instead - ``Minuit.args`` was removed, use ``Minuit.values[:]`` to get the current parameter values as a tuple - ``Minuit.values`` - Now behaves like an array instead of like a dict, i.e. methods like ``keys()`` and ``items()`` are gone and ``for x in minuit.values`` iterates over the values - Item access via index and via parameter name is supported, e.g. ``minuit.values[0]`` and ``minuit.values["a"]`` access the value for the first parameter "a" - Broadcasting is supported, e.g. ``minuit.values = 0`` sets all parameter values to 0 - Slicing is supported for setting and getting several parameter values at once - ``Minuit.errors``: see changes to ``Minuit.values`` - ``Minuit.fixed``: see changes to ``Minuit.values`` - ``Minuit.migrad`` - Keyword ``resume`` was removed; use ``Minuit.reset`` instead - Keyword ``precision`` was removed; use ``Minuit.precision`` instead - Return value is now ``self`` instead of ``self.fmin, self.params`` - ``Minuit.hesse``: Return value is now ``self`` instead of ``self.params`` - ``Minuit.minos`` - Now accepts more than one positional argument (which must be parameter names) and computes Minos errors for them - Return value is now ``self`` instead of ``self.merrors`` - ``sigma`` keyword replaced with ``cl`` to set confidence level (requires scipy) - ``Minuit.mncontour`` and ``Minuit.draw_mncontour`` - ``sigma`` keyword replaced with ``cl`` to set confidence level (requires scipy) - ``numpoints`` keyword replaced with ``size`` - Keyword arguments are keyword-only - Return value is reduced to just the contour points as a numpy array - ``Minuit.mnprofile`` and ``Minuit.draw_mnprofile`` - ``sigma`` keyword replaced with ``cl`` to set confidence level (requires scipy) - ``numpoints`` keyword replaced with ``size`` - Keyword arguments are keyword-only - ``Minuit.profile`` and ``Minuit.draw_profile`` - ``bins`` keyword replaced with ``size`` - Keyword arguments are keyword-only - ``Minuit.fitarg`` was removed; to copy state use ``m2.values = m1.values; m2.limits = m1.limits`` etc. (Minuit object may become copyable and pickleable in the future) - ``Minuit.matrix`` was removed; see ``Minuit.covariance`` - ``Minuit.covariance`` instead of a dict-like class is now an enhanced subclass of numpy.ndarray (util.Matrix) with the features: - Behaves like a numpy.ndarray in numerical computations - Rich display of the matrix in ipython and Jupyter notebook - Element access via parameter names in addition to indices, e.g. Minuit.covariance["a", "b"] access the covariance of parameters "a" and "b" - ``Minuit.covariance.correlation()`` computes the correlation matrix from the covariance matrix and returns it - Has always full rank, number of rows and columns is equal to the number of parameters even when some are fixed; elements corresponding to fixed parameters are set to zero in the matrix - ``Minuit.gcc`` was removed for lack of a known use-case (submit an issue if you need this, then it will come back) - ``Minuit.is_clean_state`` was removed; use ``Minuit.fmin is None`` instead - ``Minuit.latex_param`` was removed; LaTeX and other table formats can be produced by passing the output of ``minuit.params.to_table()`` to the external ``tabulate`` module available on PyPI - ``Minuit.latex_initial_param`` was removed; see ``Minuit.latex_param`` - ``Minuit.latex_matrix`` was removed; LaTeX and other table formats can be produced by passing the output of ``minuit.covariance.to_table()`` to the external ``tabulate`` module available on PyPI - ``Minuit.ncalls_total`` was replaced with ``Minuit.nfcn`` - ``Minuit.ngrads_total`` was replaced with ``Minuit.ngrad`` - ``Minuit.np_covariance`` is now obsolete and was removed; see ``Minuit.covariance`` - ``Minuit.np_matrix`` is now obsolete and was removed; see ``Minuit.covariance`` - ``Minuit.np_values`` was removed; use ``Minuit.values`` instead or ``np.array(m.values)`` - ``Minuit.np_errors`` was removed; use ``Minuit.errors`` instead or ``np.array(m.errors)`` - ``Minuit.np_merrors`` was removed; use ``Minuit.merrors`` or ``Minuit.params`` instead - ``Minuit.use_array_call`` was removed, ``Minuit.fcn`` and ``Minuit.grad`` always require parameter values in form of sequences, e.g. ``minuit.fcn((1, 2))`` - ``util.FMin`` is now a data class with read-only attributes, the dict-like interface was removed (methods like ``keys()``, ``items()`` are gone) - ``tolerance`` attribute was replaced with ``edm_goal``, since the effect of ``tolerance`` varies for ``Minuit.migrad`` and ``Minuit.simplex``, ``edm_goal`` is the actual value of interest - Property ``nfcn`` is the total number of function calls so far - Property ``ngrad`` is the total number of gradient calls so far - ``ngrad_total`` was removed and replaced by ``ngrad`` - ``nfcn_total`` was removed and replaced by ``nfcn`` - ``up`` was removed and replaced by ``errordef`` (to have one consistent name) - ``util.MError`` is now a data class, dict-like interface was removed (see ``util.FMin``) - ``util.Param`` is now a data class, dict-like interface was removed (see ``util.FMin``) - ``util.Matrix`` is now a subclass of a numpy.ndarray instead of a tuple of tuples - ``util.InitialParamWarning`` was removed since it is no longer used - ``util.MigradResult`` was removed since it is no longer used - ``util.arguments_from_inspect`` was removed from the public interface, it lives on as a private function New features ~~~~~~~~~~~~ - ``Minuit`` class - Now a class with `__slots__`; assigning to a non-existent attribute (e.g. because of a typo) now raises an error - Parameter names in Unicode are now fully supported, e.g. ``def fcn(α, β): ...`` works - New method ``simplex`` to minimise the function with the Nelder-Mead method - New method ``scan`` to minimise the function with a brute-force grid search (not recommended and infeasible for fits with more than a few free parameters) - New method ``reset`` reverts to the initial parameter state - New property ``limits``, an array-like view of the current parameter limits; allows to query and set limits with a behaviour analog to ``values``, ``errors`` etc.; broadcasting is supported, e.g. ``minuit.limits = (0, 1)`` makes all parameters bounded between 0 and 1 and ``minuit.limits = None`` removes all limits - New property ``precision`` to change the precision that Minuit assumes for internal calculations of derivatives - Support for calling numba-compiled functions that release the GIL (slightly more efficient already today and may be used in the future to compute derivatives in parallel) - Now pretty-prints itself in Jupyter notebooks and the ipython shell, showing the equivalent of ``Minuit.fmin``, ``Minuit.params``, ``Minuit.merrors``, ``Minuit.covariance``, whatever is available - ``util.Param`` class - New attribute ``merror``, which either returns a tuple of the lower and upper Minos error or None - All attributes are now documented inline with docstrings which can be investigated with ``pydoc`` and ``help()`` in the REPL - ``util.Params`` class - New method ``to_table``, which returns a format that can be consumed by the external ``tabulate`` Python module - ``util.FMin`` class - New attribute ``ngrad`` which is the number of gradient calls so far - New attribute ``has_parameters_at_limit`` which returns True if any parameter values is close to a limit - All attributes are now documented inline with docstrings which can be investigated with ``pydoc`` and ``help()`` in the REPL - ``util.Matrix`` class - New method ``to_table``, which returns a format that can be consumed by the external ``tabulate`` Python module - New method ``correlation``, which computes and returns the correlation matrix (also a ``util.Matrix``) Bug-fixes ~~~~~~~~~ - Calling ``Minuit.hesse`` when all parameters were fixed now raises an error instead of producing a segfault - Many life-time/memory leak issues in the iminuit interface code should be resolved now, even when there is an exception during the minimisation (there can still be errors in the underlying C++ Minuit2 library, which would have to be fixed upstream) Other changes ~~~~~~~~~~~~~ - Several attributes were replaced with properties to avoid accidental overrides and to protect against assigning invalid input, e.g. ``Minuit.tol`` and ``Minuit.errordef`` only accept positive numbers - Documentation update and clean up - Logging messages from C++ Minuit2, which are produced when ``Minuit.print_level`` is set to 1 to 3 are now properly shown inside the notebook or the Python session instead of being printed to the terminal - Assigning to ``Minuit.print_level`` changes the logging threshold for all current and future ``Minuit`` instances in the current Python session, this is not really desired but cannot be avoided since the C++ logger is a global variable - docstring parsing for ``util.describe`` was rewritten; behaviour of ``describe`` for corner cases of functions with positional and variable number of positional and keyword arguments are now well-defined - iminuit now has 100 % line coverage by unit tests 1.5.4 (November 21, 2020) -------------------------- - Fixed broken sdist package in 1.5.3 1.5.3 (November 19, 2020) -------------------------- Fixes ~~~~~ - Fixed a crash when throw_nan=True is used and the throw is triggered - Add python_requires (`#496 `_) by @henryiii - Fixed buggy display of text matrix if npar != 2 (`#493 `_) Other ~~~~~ - Switch extern Minuit2 repo to official root repo (`#500 `_), ROOT state: a5d880a434 - Add ngrad and ngrad_total to FMin display, rename ncalls to nfcn_total (`#489 `_) - Use __getattr__ to hide deprecated interface from Python help() (`#491 `_) - Improvements to tutorials by @giammi56 - Show number of gradient calls in FMin display (if nonzero) instead of errordef value Deprecated ~~~~~~~~~~ - Minuit.ncalls, use Minuit.nfcn instead - Minuit.ngrads, use Minuit.ngrad instead 1.5.2 (September 24, 2020) -------------------------- - Fixed regression of the convergence rate of ``Minuit.migrad`` for low precision cost functions by restoring a heuristic that calls Migrad several times if convergence is not reached on first try; made this heuristic configurable with `iterate` keyword - Clarify in ``FMin`` display how the EDM convergence criterion uses the EDM goal 1.5.1 (September 20, 2020) -------------------------- - Fixed mistake in *parameter at limit* warning, which did not report correctly if parameter was at the upper limit 1.5.0 (September 17, 2020) --------------------------- New features ~~~~~~~~~~~~ - New more compact function minimum display with warning about parameters at limit - Colours adjusted in HTML display to enhance contrast for people with color blindness - Allow subclasses to use ``Minuit.from_array_func`` (`#467 `_) [contributed by @kratsg] - Nicer tables on terminal thanks to unicode characters - Wrapped functions' parameters are now correctly recognized [contributed by Gonzalo] - Dark theme friendlier HTML style (`#481 `_) [based on patch by @l-althueser] Bug-Fixes ~~~~~~~~~ - Fixed reported EDM goal for really small tolerances - ``Minuit.np_merrors`` now works correctly when some parameters are fixed - Fixed HTML display of Minuit.matrix when some diagonal elements are zero Deprecated ~~~~~~~~~~ - Removed ``nsplit`` option from ``Minuit.migrad`` (`#462 `_) 1.4.9 (July, 18, 2020) ---------------------- - Fixes an error introduced in 1.4.8 in ``Minuit.minos`` when ``var`` keyword is used and at least one parameter is fixed 1.4.8 (July, 17, 2020) ---------------------- - Allow ``ncall=None`` in ``Minuit.migrad``, ``Minuit.hesse``, ``Minuit.minos`` - Deprecated ``maxcall`` argument in ``Minuit.minos``: use ``ncall`` instead 1.4.7 (July, 15, 2020) ---------------------- - Fixed: ``cost.LeastSquares`` failed when ``yerror`` is passed as list and mask is set 1.4.6 (July, 11, 2020) ---------------------- - Update to Minuit2 C++ code to ROOT v6.23-01 - Fixed: iminuit now reports an invalid fit if a cost function has only a maximum, not a minimum (fixed upstream) - Loss function in ``cost.LeastSquares`` is now mutable - Cost functions in ``cost`` now support value masks - Documentation improvements - Fixed a deprecation warning in ``Minuit.mnprofile`` - Binder now uses wheels instead of compiling current iminuit 1.4.5 (June, 25, 2020) ---------------------- - Improved pretty printing for Minos Errors object ``MErrors`` - Added docs for cost functions 1.4.4 (June, 24, 2020) ---------------------- - Reverted: create MnHesse C++ instance on the stack instead on the heap - Added least-squares cost function and tests 1.4.3 (June, 24, 2020) ---------------------- Bug-fixes ~~~~~~~~~ - Fixed a bug where running ``Minuit.hesse`` after ``Minuit.migrad``, which would ignore any changes to parameters (fixing/releasing them, changing their values, ...) - Fix number formatting issues in new quantities display - Removed engineering suffixes again in favour of standard exponential notation Deprecated ~~~~~~~~~~ - keyword `forced_parameters` in `Minuit.__init__` is deprecated, use `name` Features ~~~~~~~~ - Added general purpose cost functions for binned and unbinned maximum-likelihood estimation (normal and so called extended) Documentation ~~~~~~~~~~~~~ - Updated error computation tutorial - New tutorial which demonstrates usage of cost functions 1.4.2 (June, 14, 2020) ---------------------- Hot-fix release to correct an error in ``Minuit.merrors`` indexing. Documentation ~~~~~~~~~~~~~ - New tutorial about using Numba to parallelize and jit-compile cost functions 1.4.1 (June, 13, 2020) ---------------------- Mostly a bug-fix release, but also deprecates more old interface. Bug-fixes ~~~~~~~~~ - Fixed a bug when displaying nans in rich displays Deprecated ~~~~~~~~~~ - ``Minuit.minoserror_struct``: use ``Minuit.merrors``, which is now an alias for the former - ``Minuit.merrors`` now accepts indices and parameter names, like `Minuit.values`, etc. Features ~~~~~~~~ - Show engineering suffixes (1.23k, 123.4M, 0.8G, ...) in rich diplays instead of "scientific format", e.g. 1.23e-3 - New initial step heuristic, replaces pedantic warning about missing step sizes - New initial value heuristic for parameters with limits Documentation ~~~~~~~~~~~~~ - New tutorial about using Numba to parallelize and jit-compile cost functions 1.4.0 (June, 12, 2020) ---------------------- This release drops Python 2 support and modernizes the interface of iminuit's Minuit object to make it more pythonic. Outdated methods were deprecated and replaced with properties. Keywords in methods were made more consistent. The deprecated interface has been removed from the documentation, but is still there. Old code should still work (if not please file a bug report!). Bug-fixes ~~~~~~~~~ - Fixed an exception in the rich display when results were NaN - ``Minuit.migrad_ok()`` (now replaced by `Minuit.accurate`) now returns false if HESSE failed after MIGRAD and made the minimum invalid - Running ``Minuit.hesse()`` now properly updates the function minimum - Fixed incorrect ``hess_inv`` returned by ``minimize`` - Fixed duplicated printing of pedantic warning messages Deprecated ~~~~~~~~~~ - ``Minuit.list_of_fixed_params()``, ``Minuit.list_of_vary_params()``: use ``Minuit.fixed`` - ``Minuit.migrad_ok()``: use ``Minuit.valid`` - ``Minuit.matrix_accurate()``: use ``Minuit.accurate`` - ``Minuit.get_fmin()``: use ``Minuit.fmin`` - ``Minuit.get_param_states()``: use ``Minuit.param`` - ``Minuit.get_initial_param_states()``: use ``Minuit.init_param`` - ``Minuit.get_num_call_fcn()``: use ``Minuit.ncalls_total`` - ``Minuit.get_num_call_grad()``: use ``Minuit.ngrads_total`` - ``Minuit.print_param_states()``: use ``print`` on ``Minuit.params`` - ``Minuit.print_initial_param_states()``: use ``print`` on ``Minuit.init_params`` - ``Minuit.hesse(maxcall=...)`` keyword: use ``ncall=...`` like in ``Minuit.migrad`` - ``Minuit.edm``: use ``Minuit.fmin.edm`` New features ~~~~~~~~~~~~ - iminuit now uses the PDG formatting rule for quantities with errors - slicing and basic broadcasting support for ``Minuit.values``, ``Minuit.errors``, ``Minuit.fixed``, e.g. the following works: ``m.fixed[:] = True``, ``m.values[:2] = [1, 2]`` - ``Minuit.migrad(ncall=0)`` (the default) now uses MINUITs internal heuristic instead of a flat limit of 10000 calls - ``minimize`` now supports the ``tol`` parameter - `Minuit` now supports ``print_level=3``, which shows debug level information when MIGRAD runs - Binder support and Binder badge for tutorial notebooks added by @matthewfeickert Documentation ~~~~~~~~~~~~~ - New tutorials on error computation and on using automatic differentiation 1.3.10 (March, 31, 2020) ------------------------ Bug-fixes ~~~~~~~~~ - sdist package was broken, this was fixed by @henryiii Implementation ~~~~~~~~~~~~~~ - Allow HESSE to be called without running MIGRAD first Documentation ~~~~~~~~~~~~~ - Added tutorial to show how iminuit can compute parameter errors for other minimizers Other ~~~~~ - @henryiii added a CI test to check the sdist package and the MANIFEST 1.3.9 (March, 31, 2020) ----------------------- Bug-fixes ~~~~~~~~~ - ``Minuit.draw_contour`` now accepts an integer for ``bound`` keyword as advertised in the docs - fixed wrong EDM goal in iminuit reports, was off by factor 5 in some Interface ~~~~~~~~~ - removed the undocumented keyword "args" in ``(draw_)contour``, ``(draw_)profile`` - removed misleading "show_sigma" keyword in ``draw_contour`` - deprecated ``Minuit.is_fixed``, replaced by ``Minuit.fixed`` property - deprecated ``Minuit.set_strategy``, assign to ``Minuit.strategy`` instead - deprecated ``Minuit.set_errordef``, assign to ``Minuit.errordef`` instead - deprecated ``Minuit.set_print_level``, assign to ``Minuit.print_level`` instead - deprecated ``Minuit.print_fmin``, ``Minuit.print_matrix``, ``Minuit.print_param``, ``Minuit.print_initial_param``, ``Minuit.print_all_minos``; use ``print`` on the respective objects instead - added ``Minuit.nfit`` to get number of fitted parameters Implementation ~~~~~~~~~~~~~~ - improved style of draw_contour, draw more contour lines - increased default resolution for curves produced by ``(draw_)mncontour``, ``(draw_)contour`` - switched from internal copy of Minuit2 to including Minuit2 repository from GooFit - build improvements for windows/msvc - updated Minuit2 code to ROOT-v6.15/01 (compiler with C++11 support is now required to build iminuit) - @henryiii added support for building Python-3.8 wheels Documentation ~~~~~~~~~~~~~ - added iminuit logo - added benchmark section - expanded FAQ section - updated basic tutorial to show how parameter values can be fixed and released - added tutorial about combining iminuit with automatic differentiation - clarified the difference between ``profile`` and ``mnprofile``, ``contour`` and ``mncontour`` - fixed broken URLs for external documents - many small documentation improvements to increase consistency 1.3.8 (October 17, 2019) ------------------------ - fixed internal plotting when Minuit.from_array_func is used - documentation updates - reproduceable build 1.3.7 (June 12, 2019) --------------------- - fixed wheels support - fixed failing tests on some platforms - documentation updates 1.3.6 (May 19, 2019) -------------------- - fix for broken display of Jupyter notebooks on Github when iminuit output is shown - replaced brittle and broken REPL diplay system with standard ``_repr_html_`` and friends - wheels support - support for pypy-3.6 - documentation improvements - new integration tests to detect breaking changes in the API 1.3.5 (May 16, 2019) [do not use] --------------------------------- - release with accidental breaking change in the API, use 1.3.6 1.3.4 (May 16, 2019) [do not use] --------------------------------- - incomplete release, use 1.3.6 1.3.3 (August 13, 2018) ----------------------- - fix for broken table layout in print_param() and print_matrix() - fix for missing error report when error is raised in user function - fix of printout when ipython is used as a shell - fix of slow convergence when analytical gradient is provided - improved user guide with more detail information and improved structure 1.3.2 (August 5, 2018) ---------------------- - allow fixing parameter by setting limits (x, x) with some value x - better defaults for maxcall arguments of hesse() and minos() - nicer output for print_matrix() - bug-fix: covariance matrix reported by iminuit was broken when some parameters were fixed - bug-fix: segfault when something in PythonCaller raised an exception 1.3.1 (July 10, 2018) --------------------- - fixed failing tests when only you installed iminuit with pip and don't have Cython installed 1.3 (July 5, 2018) ------------------ - iminuit 1.3 is a big release, there are many improvements. All users are encouraged to update. - Python 2.7 as well as Python 3.5 or later are supported, on Linux, MacOS and Windows. - Source packages are available for PyPI/pip and we maintain binary package for conda (see :ref:`install`). - The bundled Minuit C++ library has been updated to the latest version (takend from ROOT 6.12.06). - The documentation has been mostly re-written. To learn about iminuit and all the new features, read the :ref:`tutorials`. - Numpy is now a core dependency, required to compile iminuit. - For Numpy users, a second callback function interface and a ``Minuit.from_array_func`` constructor was added, where the parameters are passed as an array. - Results are now also available as Numpy arrays, e.g. ``np_values``, ``np_errors`` and ``np_covariance``. - A wrapper function ``minimize`` for the MIGRAD optimiser was added, that has the same arguments and return value format as ``scipy.optimize.minimize``. - Support for analytical gradients has been added, users can pass a ``grad`` callback function. This works, but for unknown reasons doesn't lead to performance improvements yet. If you can help debug or fix this issue, please comment `here `__. - Several issues have been fixed. A complete list of issues and pull requests that went into the 1.3 release is `here `__. Previously ---------- - For iminuit releases before v1.3, we did not fill a change log. - To summarise: the first iminuit release was v1.0 in Dec 2012. In 2013 there were several releases, and in Jan 2014 the v1.1.1 release was made. After that development was mostly inactive, except for the v1.2 release in Nov 2015. - The release history is available here: https://pypi.org/project/iminuit/#history - The git history and pull requests are here: https://github.com/scikit-hep/iminuit iminuit-2.24.0/doc/citation.rst0000644000000000000000000000011314332717401013311 0ustar00.. include:: bibliography.txt .. _citation: .. include:: ../CITATION.rst iminuit-2.24.0/doc/conf.py0000644000000000000000000000503214332717401012251 0ustar00import os from iminuit import __version__ as version import sys sys.path.append(".") from root_version import root_version # noqa # release and version are special variables used by sphinx with open("../README.rst") as f: readme_content = f.read() readme_content = readme_content.replace("doc/", "") readme_content = readme_content.replace( ".. version-marker-do-not-remove", "**These docs are for iminuit version:** |release|", ) with open("index.rst.in") as f: index_content = f.read() with open("index.rst", "w") as f: f.write(readme_content + index_content) release = f"{version} compiled with ROOT-{root_version}" project = "iminuit" copyright = "2022, Hans Dembinski and the iminuit team" # 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.napoleon", "sphinx.ext.autodoc", "sphinx.ext.mathjax", "sphinx.ext.autosummary", # 'matplotlib.sphinxext.only_directives', "matplotlib.sphinxext.plot_directive", "nbsphinx", "IPython.sphinxext.ipython_console_highlighting", ] nbsphinx_kernel_name = "python3" nbsphinx_execute_arguments = [ "--InlineBackend.figure_formats={'svg', 'pdf'}", "--InlineBackend.rc=figure.dpi=96", ] nbsphinx_execute = "auto" # use FAST=1 to speed up doc build if bool(os.environ.get("FAST", False)): nbsphinx_execute = "never" autoclass_content = "both" autosummary_generate = True autodoc_member_order = "groupwise" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build", "_themes"] # html_logo = "_static/iminuit_logo.svg" # 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"] # http://read-the-docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get("READTHEDOCS", None) == "True" if not on_rtd: # Import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] nbsphinx_kernel_name = "python3" nbsphinx_execute_arguments = [ "--InlineBackend.figure_formats={'svg', 'pdf'}", "--InlineBackend.rc=figure.dpi=96", ] iminuit-2.24.0/doc/contribute.rst0000644000000000000000000000570714332717401013673 0ustar00.. include:: bibliography.txt .. _contribute: Contribute ========== You can help ------------ Please open issues and feature requests on `Github`_. We respond quickly. * Documentation. Tell us what's missing, what's incorrect or misleading. * Tests. If you have an example that shows a bug or problem, please file an issue! * Performance. If you are a C/cython/python hacker and see a way to make the code faster, let us know! Direct contributions related to these items are welcome, too! If you want to contribute, please `fork the project on Github `_, develop your change and then make a `pull request `_. This allows us to review and discuss the change with you, which makes the integration very smooth. Development setup ----------------- git +++ To hack on ``iminuit``, start by cloning the repository from `Github`_: .. code-block:: bash git clone --recursive https://github.com/scikit-hep/iminuit.git cd iminuit It is a good idea to develop your feature in a separate branch, so that your develop branch remains clean and can follow our develop branch. .. code-block:: bash git checkout -b my_cool_feature develop Now you are in a feature branch, commit your edits here. venv ++++ You have the source code now, but you also want to build and test. We recommend to make a dedicated virtual environment for ``iminuit``, separate from the Python installation you use for other projects. The recommended way is to use `Python virtual environments`_ and ``pip``. .. code-block:: bash python -m venv py3 # folders called py* are ignored by .gitignore source py3/bin/activate pip install -e '.[test]' To delete the virtual environment just delete the folder ``py3``. Development workflow -------------------- To simplify hacking on OSX and Linux, we have a Makefile with common commands. Build ``iminuit`` in-place and run the tests: .. code-block:: bash make test Same and generate a coverage report: .. code-block:: bash make cov htmlcov/index.htm Build the docs: .. code-block:: bash make doc doc/_build/html/index.html Run the notebook tests: .. code-block:: bash make tutorial Maintainers that prepare a release, should follow the instructions in `doc/README.md`_ To check your ``iminuit`` version number and install location: .. code-block:: bash $ python >>> import iminuit >>> iminuit # install location is printed >>> iminuit.__version__ # version number is printed .. _conda: https://conda.io/ .. _miniconda: https://conda.io/en/latest/miniconda.html .. _Python virtual environments: http://docs.python-guide.org/en/latest/dev/virtualenvs/ .. _Github: https://github.com/scikit-hep/iminuit .. _Makefile: https://github.com/scikit-hep/iminuit/blob/main/Makefile .. _doc/README.md: https://github.com/scikit-hep/iminuit/blob/main/doc/README.md iminuit-2.24.0/doc/faq.rst0000644000000000000000000001664314332717401012265 0ustar00.. include:: bibliography.txt .. _faq: .. currentmodule:: iminuit FAQ === Disclaimer: Read the excellent MINUIT user guide! ------------------------------------------------- Many technical questions are nicely covered by the `Minuit reference manual `_ of the original MINUIT in Fortran, on which MINUIT2 is based. Still relevant is chapter 5, which covers all the questions that one encounters when using Minuit in practice. If you have trouble with the fit, there is advice in sections 5.6 and 5.7 on how to fix common issues. Another source is the :download:`MINUIT User's guide `, which covers the C++ Minuit2 library on which iminuit is based. It also contains advice on troubleshooting, we will frequently refer to it here. However, the Fortran manual usually goes into more detail and is therefore preferred. I don't understand :meth:`Minuit.hesse`, :meth:`Minuit.minos`, :attr:`Minuit.errordef`; what do these do? ----------------------------------------------------------------------------------------------------------- How do I interpret the parameter errors? ---------------------------------------- How do I obtain a high-quality error matrix? -------------------------------------------- What effect has setting limits on the parameter uncertainties? -------------------------------------------------------------- The MINUIT2 user's guide explains all about it, see pages 6-8 and 38-40. I want to cite iminuit in my paper. Help? ------------------------------------------------ We use Zenodo to make each iminuit release citable. You can either cite iminuit as a software or you can cite the exact version that was used for your analysis. For more details, see :ref:`citation`. Can I have parameter limits that depend on each other (e.g. x^2 + y^2 < 3)? --------------------------------------------------------------------------- MINUIT was only designed to handle box constrains, meaning that the limits on the parameters are independent of each other and constant during the minimization. If you want limits that depend on each other, you have three options (all with caveats), which are listed in increasing order of difficulty: 1) Change the variables so that the limits become independent. For example, transform from Cartesian coordinates to polar coordinates for a circle. This is not always possible, of course. 2) Use another minimizer to locate the minimum which supports complex boundaries. The `nlopt`_ library and `scipy.optimize`_ have such minimizers. Once the minimum is found and if it is not near the boundary, place box constraints around the minimum and run iminuit to get the uncertainties (make sure that the box constraints are not too tight around the minimum). Neither `nlopt`_ nor `scipy`_ can give you the uncertainties. 3) Artificially increase the negative log-likelihood in the forbidden region. This is not as easy as it sounds. The third method done properly is known as the `interior point or barrier method `_. A glance at the Wikipedia article shows that one has to either run a series of minimizations with iminuit (and find a clever way of knowing when to stop) or implement this properly at the level of a Newton step, which would require changes to the complex and convoluted internals of MINUIT2. Warning: you cannot just add a large value to the likelihood when the parameter boundary is violated. MIGRAD expects the likelihood function to be differential everywhere, because it uses the gradient of the likelihood to go downhill. The derivative at a discrete step is infinity and zero in the forbidden region. MIGRAD does not like this at all. What happens when I change the strategy? ---------------------------------------- See page 5 of the MINUIT2 user guide for a rough explanation what this does. Here is our detailed explanation, extracted from looking into the source code and doing benchmarks. * ``strategy = 0`` is the fastest and the number of function calls required to minimize scales linearly with the number of fitted parameters. The Hesse matrix is not computed during the minimization (only an approximation that is continuously updated). When the number of fitted parameters > 10, you should prefer this strategy. * ``strategy = 1`` (default) is medium in speed. The number of function calls required scales quadratically with the number of fitted parameters. The different scales come from the fact that the Hesse matrix is explicitly computed in a Newton step, if Minuit detects significant correlations between parameters. * ``strategy = 2`` has the same quadratic scaling as strategy 1 but is even slower. The Hesse matrix is always explicitly computed in each Newton step. If you have a function that is everywhere analytical and the Hesse matrix varies not too much, strategy 0 should give you good results. Strategy 1 and 2 are better when these conditions are not given. The approximated Hesse matrix can become distorted in this case. The explicit computation of the Hesse matrix may help Minuit to recover after passing through such a region. How do I get Hesse errors for sigma=2, 3, ...? ---------------------------------------------- For a least-squares function, you use ``errordef = sigma ** 2`` and for a negative log-likelihood function you use ``errordef = 0.5 * sigma ** 2``. Why do extra messages appear in the terminal when I use `print_level=2` or larger? ------------------------------------------------------------------------------------ A `print_level=2` or higher activates internal debug messages directly from C++ MINUIT, which we cannot capture and print nicely in a Jupyter notebook, sorry. Is it possible to stop iminuit by setting a tolerance for changes in the minimized function or the parameters? -------------------------------------------------------------------------------------------------------------------------- No. MINUIT only uses the *Estimated-Distance-to-Minimum* (EDM) stopping criterion. It stops, if the difference between the actual function value at the estimated minimum location and the estimated function value at that location, based on MINUIT's internal parabolic approximation of the function, is small. This criterion depends strongly on the numerically computed first and second derivatives. Therefore, problems with MINUIT's convergence are often related to numerical issues when the first and second derivatives are computed. More information about the EDM criterion can be found in the `MINUIT paper`_. I am not sure if minimization or error estimation is reliable. What can I do? ----------------------------------------------------------------------------- Plot the likelihood profile around the minimum as explained in the basic tutorial. Check that the parameter estimate is at the bottom. If the minimum is not parabolic, you may want to use MINOS to estimate the uncertainty interval instead of HESSE. Why do you shout at me with CAPITAL LETTERS so much? ---------------------------------------------------- Sorry, that's just how it was in the old FORTRAN days from which MINUIT originates. Computers were incredibly loud back then and everyone was shouting all the time! Seriously though: People were using typewriters. There was only a single font and no way to make letters italic or even bold, to set apart normal text from code. So people used CAPITAL LETTERS a lot for code in technical writing. For these historic reasons, MINUIT, MIGRAD, and HESSE are often written with capital letters here. iminuit-2.24.0/doc/index.rst.in0000644000000000000000000000027014332717401013217 0ustar00.. include:: bibliography.txt .. toctree:: :hidden: about install reference tutorials studies faq changelog benchmark contribute citation iminuit-2.24.0/doc/install.rst0000644000000000000000000000253214332717401013154 0ustar00.. include:: bibliography.txt .. _install: Installation ============ Note: iminuit is tested to work with PyPy3.5 and PyPy3.6, but we do not provide binary packages for PyPy. PyPy users need to install the source package of iminuit. This happens automatically when you install it via ``conda`` or ``pip``, but requires a working C++ compiler. pip --- To install the latest stable version from https://pypi.org/project/iminuit/ with ``pip``: .. code-block:: bash $ pip install iminuit If your platform is not supported by a binary wheel, ``pip install`` requires that you have a C++ compiler available but otherwise runs the compilation automatically. Conda ----- We also provide binary packages for ``conda`` users via https://anaconda.org/conda-forge/iminuit: .. code-block:: bash $ conda install -c conda-forge iminuit The ``conda`` packages are semi-automatically maintained and usually quickly support the least Python version on all platforms. Installing from source ---------------------- For users +++++++++ If you need the latest unreleased version, you can download and install directly from Github. The easiest way is to use pip. pip install iminuit@git+https://github.com/scikit-hep/iminuit@develop This requires a C++ compiler with C++14 support. For contributors/developers +++++++++++++++++++++++++++ See :ref:`contribute`. iminuit-2.24.0/doc/mnusersguide.pdf0000644000000000000000000077375014332717401014202 0ustar00%PDF-1.2 %쏢 6 0 obj <> stream xUPMO!+Ywdմ&]iG@ mi{ǸH)NhR,oendstream endobj 7 0 obj 298 endobj 17 0 obj <> endobj 18 0 obj <> endobj 20 0 obj <> stream xYMܸ l[Y'`;;E;9ݚiR[Rۙ)Ţ#; x)5Y^ʂmJW>nXXKv͟o^\)8Srssw66F֛Wn~be3i?ԟ,R*N{ } *l.PMaUmğ3t, J~뛵S$+,GkV.Ipm}VZ&K> ezwߣyzXᔊR͖Bf^,w 'v}O8D7 ZrPjZXbײ0^۰& ͝]IN`QPuXI9yQ|k֌K8 ΩBAҖU{{jX`^ZyJ? 8#۩ٞ:S=[ `|Y;wh.J&ߒ AƄrPUXn^|uf )R}nq>#ޮj~ HQXI{)jrVY0IyZ! PB^lUa _(Bbd тG7~{nל\ t::fW%K>=V5v6u  /'zp6IIm)y^=A-g C0HCmCM8vhƏj#-2g氵m>#!Z9c@s)&{d)ȴ50y"Evt"M!-1A ׄ+f7N'qj1= L@00KAt1F9JCM,@i{c}g䌢0 u`Т(_zBjFTd՗ ;Ek =ʴ Dy?ITMLV&Lo )g+ 9@Znv}X= yI}m}$$:C=e Olc%Q*JCC$8g.A]CRQP<7Z>?7 TƴiAvEGke 5ԮEaeJp ʌ]+[D2p۳iһ[Mu}8[ߘ.@ș۷C_m'Ks!< a'P2_D1NlQWC긚'C'*˒Cd1 9v%ɜL@`9'J/| QGH C"}fXf^[h]TQ p4e=Q=8Hm Ȭ Zg&HC[!e%BwL^pC^]=F4*ENUfo6$ld p8Y֜c$YLv->;!U FcRTj0-̂D+UWd!w1`HH8xh G(%K)4 /ګM2ʢ:%]G%̜Q[lB1i?^X/1`T@NmҰjxF$Lz]JX䋘=X̜ D7.5T,ϧ4D2Ky1/bci|kꔦ!޸qо h cHQ7|N<$5WYF0P4LYLXu۟GL$;Y0-2%6MF(.  kĔgl|dF'L//z3VXkME~e[>KsH3ZbCٵCzк1sR?5ӁdIo_%weӺGULw^UZd󵭴޾N{ju'`W c/߽yo\Lx Lqm ] }&UXuԌ^ҀE: ,O IO+HZ >%Xuvx%)v 5cx#$pe=>_|f]: J-I^/VMbtmj#,юxꧫTmendstream endobj 21 0 obj 2669 endobj 37 0 obj <> endobj 39 0 obj <> stream x[r6}#NF>֭i&UȔFn|}W[y\9=<$88{ aD ףOOc_/Qdzȿ@Jb$x=(7?1̮GϚ5RS26ֶƄ #v'`J׮#TDev&Fz>5":~gj&2DrftfFTj#B*wf5bhvaۈ@L3 EwD#.AO-O`7/&̞Ber9Fb⼼`V{.P~^W04S?R6,*CG-il7E}!E/X~/`E:;z; ?s>5@Fr3Ϸ~) q KDy#"aJ97'TlS|XS&DtW &6R!;%b: .`4M|/B2fbi~ӿ )EB$0p.>~bY؉'"m^T|ixcOx+6\^<>hH";'=ZGl}1ju&:i{s1E۬9<?*79i|&L91BT٦^:@e@xÈӄE!jFʪXANlmn=َx{{ţQv yxxv=+ӌ U0LuIBG>g)X$33PzX#}l×$B(y w./A[m)y//Ԅ( i8Ov]F&a)i&j BlNl#U "\+8ojg/WOhO;!#!ΐL-$#~f޾`+lbtAtq0PzR{ $j)=?zlJڇqdxJR'օ t,hn*:NӞftּ}D.iivOpA"lOo C3OMlNNkb}O]x'| g3~d8fxB<@t1گҖ&e]4e8&tmI[jS=h:_Ty8ԁ4d#)lZm - QEU=yȡTSBhGx#kAI| N!MyUR&u#U:qe/CPw;M"E ׿"O1{Št'M&0W:Qjh#pDx;b 7qZ 90۹77䗁tQ#J.QJD$>AFB6$buZ߹zg$Xg_Zd%-yUU5;l`YB{0 }׋9'Lܰܗbyo. v~bLl>>%aK"<%}-Kpg/P%Ψ:uT%(Ud0$LP鯐4ƫRw[uqp$/qv8/{@ΏXoe0gHR EZYd:3`*](?5f\o 9lKj[ hA~_^endstream endobj 40 0 obj 1729 endobj 44 0 obj <> endobj 46 0 obj <> stream x[Ko7 :P3|?@ⶀPڋ/y娐VJrDE,E.,F_8]¿n{>!AQ#EJb$e01D^Q2 .{Go5p}ʐ8}5qXdH0r<ΐRR5DѴi"dMʬ]Lvkcz"T&cҀcJt̀$Hل$BdPa3͓̹Gš_]8β!"\],/ONnҡ!H`<"w(%G!^(6:8luF"H0׻gzu:D!…bX =:rPv Xjn8j`1,~!/]awN!7R@:MtIߊJzʓqZTpFSQ;F#-T#uYvaB=d!Aʩ_101NILe/\3p*s8L-ŵzdq6&BpZɦz5bgNJQ2t*cd{LY -ꢸγ/448*BT`sTVT<}vR="RFútv&Lkq hH2z6]KdŜb ~ 3qڽL,\@2 /,3|ZDda/6coIe4]Seb@/4X rV{@C7cQ22IqI: [E 'S?* ieBpO_ dRuCKBTIDqѳ/b*^in8tE EE"ɕf -iB&M{P]l1_407&a#dx# #! ĸF`$tY!u!|gLP-R`@HWK0q9 :L^4s=WMoM!āvwH>9TLz̤70+ >@gб2T&tۯXg[mZڌFL`3iTY7JHU\t2Llc"&=fHyK3vРhpgTdR>r^g9&T VtU SX&ES5hOI "!Uy^/ VAۈ:|;Ea$|_P*]\lnd,/.HyU6QSu˼"o3xm_Ho1yˤ`9oHYv5L+plT~`{;Y9XC[y5 Xe$%,7R@KaM {Z@4R߰2e7_ \D!eۤ!gp bqT6oo#頎GOh%ѸO7)@^'妏\9ܫQ]OA;G-1j5Ű]+G7dHAL! ^^N7&FFˏe[㪉([9d yQEoP eVҲ'h=Qa+Gg3%fo> endobj 53 0 obj <> stream xMo6:)U4iaSuE'ԻX"OvQDŽ RXM;PZo0?D޴IQD_$0s]L -h|X+(|1CXK81wCv$\} Xea* dUuKH%+B[L~sP 8<@8y$}agMf}qB t.ɳt[Lb7ۑj-}Mx6qR @NTtP04hӾ~aHb@hzj2gjnkVXG''{.K};L˰x11NVNGqC.}(p&avW2gYEŜ8²Ȑ+EɛMILBCf_$y#zF"K}yGeekSY"QͯEC: f>(OPPRroV4JTz}:O@E{PaMP=QYϞ_G#5 ]T}idSR5qZT6t}eDoLہ.El@I6\ߊǵɤƬb7$*P/}1]bNnWLFr\2X*h&MӪmK驳q|i~@@ hRTiSt!#9slE7u .au+TĖ]Js 9qxHu}{Kv}I"%endstream endobj 54 0 obj 1341 endobj 55 0 obj <> endobj 57 0 obj <> stream x[KoF :o0nZVbmPwR)$pw#2O{}}+s?t<{M c Rt$FR'Gs3F¼ltcpWqlh<שHDAA.| *1e++(Z@ Kk`e1fH.[H X`x|kr&`n|4Hٺ8Rۗٙm3#HTRdI 5I:m?8(ѿa{RHiC h'!GX2Q3*j22\g4BZ9bu|-`m34g^}iܪsm;! 2P`7tz?TP]Fۋ2^Y:akd&m _XFȆ:AzvuNזعDsr HKڷ8U9+3)<ƛfM+dۺ*WÏ7]wsД 0D vs˻pw!Űgv@D ̮㏫0~IOۑ ;I,uکNk.Hpqwsgbg;jUّH!5u=7&}}(%Jߧb&uVۭ`RlJ}@!h&hji~/mJյtCOj)T!!8s rƴj Z.Gq &6$L>Wpst6YB{{ Nle?E=1ia]-gK)IIKE7ĺK纡PB-ضab6{I!E<3:몿}KY&>KJiKHQnZym]43[go0o7 d0:uB>U'0>62$CF P\X , {yj%(s{]mA+2!;OV:¼ f5 =)-1RאvX 9z)3pQ7ԵXz$YH鰙&\ShfO"cb]ne1bu~keR^cTю$j_qcn橣816ʨ-hW;)5?6#PU}5[0&ЙKEquv~*ھu a1nO9&()O`D}bK7'TStendstream endobj 58 0 obj 1556 endobj 59 0 obj <> endobj 61 0 obj <> stream x[mo6 R  4E_nED-;R˒zqlF>$8G#cSz_z_;" #.{y' EJb$e4pD82OFCԇ)Čo4靤, _E(_ .䝖 aB|'dDѴ"B0 eV[6UQÂPO$BĨ "HsP0Fpt `zA$:afj QqUa&NFB)$`nRtǯv;e .݌(l tC#T*!`M|'~@$`9PF{1UJdd<07!Е+*%) : YuVqwQGp)r.r-axq2Ny}y69= Ǚc\"9 4Kq1M&4U[flSg<@]p| ItW@Ul9Қ>-}ȿwO]߼QCQ[7z.Y4z6FyXΣ1vbE'.8C%NrQ l9w[Vq7s>| .NHָCJF}dMva9.EI\Tj%+X ,%] r3.ɧ`zv ECCFK.ZhѶАv`M06'us`;3枡B8[fgPI{Dp+;m0y!NfT q܄"B0@#-sRw Wo(rq3tcd v9wѱn5gB1e{iA^Bp%HlmLU\eda0˫H#FpCIk&gQGq45ԏm;DʅlSH?D jhbe˴(vE\A;\w'>mʊ]> endobj 65 0 obj <> stream xYMoF5~Ӵ4A3b! I)3䒢H;jՑj``yfk/].&-Et='2&!h8R4V#)E=}#e4,Zg8BBpF[SR&mp6}Id69AM&BFUm:AcL/ }{"T&r+XW 2Rn/m]!EAP\8Oʐ$_;Fh}iN ]ʄJu[3sWFP S$rHm6 ?}2n[EِD){({&SէD7)1ڝ0%|L76cX8$_څ Avc*O#bt'5g5~kzCA#D3=]1,0b$1dy[:?ܽB-n qGx>4>NI1=21՚u`tl S%͛2>¸<8T_!p>#Mަn?$b6yLs)-XhToEj8(,=vTuf@pֳ M9+)U/ǍESؘ!?!ŻDK\H]g/L(^}|(!0JwfڞAUZ>l@%aeqɥf>|PjU꨻{Pbvc?T?4pl2dq$quqNWWV1 -`ȳQ0Ađd*gYd D(f:yG7 Qq6 b(e)L6eFlm5M !"ú>X})taTp}N=yc꦳wk/{uq_{|`> endobj 69 0 obj <> stream xœN0 y܀CM'Nr MUֱ!:f1BC/8]:^G1~/RW :uPv}T) h:\N;f .NO6Ĝ5"DLg^XdaؐBC1dj$HoR$OvÏidKg) E G|@&U'"&:g(%\fuoNn. G ҡ(n˒P.!?wО]7#6r /OlUO V_̧-f,_M&42ʥGWmB>ApG`)M;IM@ ޢJ_K+endstream endobj 70 0 obj 387 endobj 71 0 obj <> endobj 73 0 obj <> stream x[MDc'lMU/{`a9h53VR{<}ʒz=D`.2_|UXsⷅ?ū? " 1s/i Z]<gjg z:,~6R_GXĢ2;1,̤&~\ѰJ îYāqvw}&(BL* \vq)M0^dF%LruW̕6W~gWWdכ6eL.nVc٬Y9ZәR5^,-s妟(p3 9sW-ɹBy۰ l^2k+\2\r-[ 8f0%Leѣúovվ}+vr,?^2,IZF.J'netA2. u^>ZQ3\`VٲZ~)]-a.ы7㔌Kk7kȬ3qweU6sf>2YQظ/_3gBf񼱘>`lH2o?ɆH2^hE@ۧ&g |00 Ryx~#P0eojaEQIBg㕭!HqkREs O \z{Y*Xh.=uruu8c:H Mn*@Vc ^y *[.U+(l&PYtU c-pĖf}uv6Ћ Ǽ@B~)lDBwge p9U?t i"؈1@1ؒ`rvqu㍜(?˩,`Jpo^Ѽd!׫|Z֬`bIpB> Qe'ʜ1XBWycgn]oǂ]*b[ x|)_GǘΔ"R )r1` yGH-x- F*C" 猏ZȍfA^ l_JW:.1F!"$b!X3<հN\[2VIZ [$nUSu\q}{ tGfI7nĈsUE)h}EWx95%*Rj9x%-ɫP!8ATJ+>/-)ya TB8Ij:ʛ)x-4*2p2qblC h<5^F72} eVe@P\ WPAYXy r<,݉}N9*Fև8%B&+I[νӞ :2x{`[o}S$̹2@Yfbj##c  q M9zƺG m#2YT =PNKoV0\F>OTyUZsO (V1>{kMTIWm#Giivko!pv|R9;j|$YԨ(b Z%zRq9cW@au8-ov(,, ~_)˰5At~ڀ6 $ycښG.T(1D ŜqkvCH`Rgڷ!@H{4s=jWU_Q&R[OˈG-5o圗QwIqTnR66nS2Q;raXW Xԁ)^(9Xg9[?B,aNII]5QR"KJ|ZOcݩOs۬^ƆV\έ{R B% Y5_P+S*|^q,s@ꓖO37%hvh&eIp>ixH·zsDIB*mU3_OCxB*6:S%w8=g ^Q(Hs8ӭ!&J$ 3"RӯVp > ~TrS!&DGbgMVyF.q HE&bf9d4o}8"$&-8eҔPu.uj'UlVqq"BQh/oلT'DQժHn8ѾԹ"1?NMG]RNLsM盪fݎ:per3)s$%'2[\-VIa3òPKll07~_/wS7|au^ڴ{9[˵)B/iǮk5I<,0& 6+I̯c7um,XH>O=p8 65BC*kP#6^ ٝt,t4S.cc$e5tE8zLdX0|Ɉ` (23C&&W}>v T?3Hw;ɟ3]v ng8B!MHb;(S102^RkWa?fݺ ) [dj{88S6ڒOWhC4PG@ ұ.qLݨSEe'J͗YzJVCʊag:A%kVsΔ0;faǬ~Pc-K gBr)Wo.^zxB\տ_7/vś0=s֠`=s eEsv Gp}6%>:ɷ^o#IVsGe\|:8M,; \K.q&6ŹRkZP)ugk@%tG;|p`b0˜}d~ؾg[^GD3݇DsOlÑX*|;w6'%4G䡊7ϜLUl6>&ޚ9論_-U7oSuX ]*̍EI4h>6)0)?L( LQ\D)q ez_hSg#ש@Av%)mEBsZ=l~:u*okvfx&!ȩ,dNs endstream endobj 74 0 obj 3981 endobj 90 0 obj <> endobj 92 0 obj <> stream xYM\We|+vEU*V!DbAU6>=_=rf~=[dYf$n-?o?$0Bf_ K T,\B3v'`”-%_AL<<3šQapC7i6muA #gSXFv0v}7w[}BUm{ aNAwejEMA]_@/x,ov ĒH(++*|&%cmb U[߅D)qnZ1jM:,`OC;eDGK,Wi 4U>[< +\_])"N5&5XA {l j#&ʣ*Ous302lBTJea-dX /f}6GG* #ű_~q b%)9 \݉$ ~!9&} yUx$o*W6 H]n#b/ZT8ԙ^ Ӿ# Nf0W{qw/;Tr eL x9gt.|:C`@yyS뇦;\"4E r폧0H gu(%ZWT0Oven2]1Gc2iv@D1ы0^$뱭6A Lv6I\ۚA9 =*zGE .uC,#bP[#S" II: 0WBTd%V_^é# (5(qLc}5B҄_> ;H~J_[) 6怖 6[6"x&ćT2iX >~Xr/c{q^*6FW7^AV 2STt;1D:jGޱoDN8|й$ɁI)L¶극v ^K8\j@)m>U@=J8߮8&ϊFyŠF&>:4YM j ?<2k '1e:A5ia>*IhBm36UvXր_,}d6ص'?4TRPE:A3~:T|~le]ZwY_1BKnSrloOCT6CΫ~ZyĪ5U na| ҨٓET`Te%Bc2S" -Ezwl%vBF-TT*v^pjؐj}i'Ž9g#7דOˍy$ɩfd] Řw1(1+qti "ߜv6C W&F-A-6̤q3?yLAjodr=@#2|\V(/+f,ZgaםZWL&MoவKȨJ4,]@'g>< u!4%FO+JM=[h`~7 +kLb Q0Nsޏ=x Κ/90MOZCa-ņr^{.B*8}8D#ONaz`7IҎD$Y8 ސ{8nu!T/#s D?p,BfNVY٦?͒8.pXr 1}L?щrY"/r5Qd;D+fqԚLvcF>DC~ypf'K2s׵2^%ƒ0|nk?7@o5<3R{l@WvaoB&wBf@?fv/G W6_^y%ĉendstream endobj 93 0 obj 2156 endobj 94 0 obj <> endobj 96 0 obj <> stream xYK// |Nr!~ē 52!WLVw5G@]]W_)a ҿibqJ&`I17?}73Dzal_ <%G4J?a]Yqtq$I W`DVT-DX>2SWa2t f]"Eo^%Dcm"Rrh9ڌ9/|8e kD&Rp1F mЦ0/Cfϣ RL+<dM<8}2l56ɒ xpxKpCKtV21N%PݯJb(OV!Xץ +Ī<`VۄMy.fTN%KsSyxXi\[}jxeU&\"rH> >uo 9_ɗNpΎ3Mf?= Td\DŽ_锢jdPqm 9ohSyTpDW='Cr}q~|Ss ,4XC0+OPflx6:$<<)$>Y<2O>NANEtH'>$@K$F|(ar!$ hi@W;uL<8>)Nq O8o)">1_usy?z{* 8e_p _I! ySUWK"dyUr iﲽ+NidD*y>-73ɖL,KPq:pºiI K}.{sM ]e"a.W]HAG lytiKw s.Iͥ1]OX S@}$j9ͮz+˯bN DςmO6]tYr ̅j/x!% ##'*S,X%]*]_R)"E)!խ13Tx(yJHu:gqL3В8!Gn VEʵ.% ؜\lvCޢ#W 5WɉfJW%ѻ_/1eO$FYL!XpBHPm!.k{a[7Bkbc ,m SQ$/oRag%5$hbA\![%TwA@ch4J$B1Vzy講!O4c,J5V]2]١M!ts_~tD[ ]vk3U6nLu/$ T@~@ d@}(=qt]P1XnmYb =S|ި(B`Hb&!73,K:S i,NEA:窩oN$?_>^2DkA`$'Fgs e$&&#]7@_{xĵ./9 1P 8vYAyx~^YLi^#/u mp (gVn/㰔^]3tپH8O5tq29f̈́I"͗2 /=r;UPW}6E. jTiѹ]2pMifٚ*4yu_E,-%}`dE.I{L{ ʒ"s;r#"4F`wdjqZ[өz+8l#ؐƅBMF-&?ꀢ p9w^:K/J0N[* GDfQ?\ L“l@uYOCtMpq],SO!M3L.N^ӡT&_y)O-nN׃IǕ~Oɍh/&+ԏ:rQx' IӜ+s9pon_e&[0O`X5ylaz|gXnTtJp՝z倳e~1.MF'i?&g{b=iѷ葥Oϗ!B@ qܸ4^nކ,R..4 i10iy}b+ܕqw]⬹ x6/\SiB@x"J|o^Rendstream endobj 97 0 obj 2459 endobj 98 0 obj <> endobj 100 0 obj <> stream x[Kym @(ZCBpLH}Q${V$.Yϯj [zwՊgnWbXۻdWF֫Փ?=/mXvsfﰶVxtnh,Cwv=TOƴΥC卓God愽p4ȗ >J4k.R%{56E3Q pHǒ6Lq-Inyl[c057Ƶp"wV՜)CKgz k)1F625%m킞o0BRWP]˗JMBזyqnyWV$ZR>! y~Bk8Ixs?(%s(LJ Yc4KQ}w8F/ƹeYKRJ%,SJl2y^L~AnMԮ;߼+" { 1#Nc@IF2} '۷1ޯ !q^l 4LkW ka";/+C6/̑5\H#jop5db߿ $j C Ew\u<D뻾;AG eƔbEJ*C/?)"# WfoBMiW&sB$ ,RḦ,/X[K$CM>PbcFͶ9Cy:_R:Uj91QPc;&n; Cmx]pa};J@-R"D[PZ&(fBܻ~BtQ "@XFP^[/2XʤP|d-/sc쭡EX, 12AcХɫ /}i41$?%Ʈ_ BV}@VsyY1Io!e9BK8hxfuˁ;6} 2U1scTmK0,* LJ0K~w:AB'o!YOc#@ka576ּ፦laxq f#-x`CAu*_kS*7C7pq8 2f-4ߦa"@ 9Z"]ܚRxX.Q,[*plDgڤ)CB1 hDg^#086Ȇ(1M$c%4GwwoFj9I hEKV{j)L$a;zR ܢ>'~z;Z9c@P23N5O^f$$ynPټwKBJG"1@B˗P9:X/Uӏy6ڳ }'֊@hcEv $8R) rX(vl'c3!Txn΢?!Weolf>V:F:.X['/b^:is_& jl83r.\ {铷Gx:vdGM ~^W]A3cT bfN- p58Dӑ&3`X)``8Aa ~jR!oXKLa\7yUc(~NCN2#2/jb`| :CDMQOaS?AC,xEkP~*k,ɻCB HPfHpAh,/WV%vu ^_ .4LwE\}ut9:SO3lN]O‚F\y'X .yӴZ|Fh2}X_^0cX{>a0nZHJFj dv4c0ka{TP]0nZ}!JC7A)NB3jfcj׹шo<\@o]h~$*Xń9lx!unռrXfb*KpK/C.eI>B@ k(X1zT(]S+9'w eBAh>RU4uִ az2U2#l46uM4+@FV\;#G&pDZwN\ ؿ:aZ$zl\,d!@y,P#1|%k7oE! N[V#) Ex1Ґ@ "̇Hu@9xEV . Z:=c02g&Ru$jnCo;Fn6Zw:ar*kԤԾ}u;RlZqGݕyJp^Hu.ե˼7 oE9?T: QD&~_N 4,yʔ5x87ϣٙᏬ*q P>F %+ӛ&\"f֛h`Eٙx5N"Oikia URYOWk'!++U !sPP=ʭ%}S^'‚Z* ƻ,KEX# Sp%JOXNEg)ey?#`B-9 rV^7Ihӭihn :̾ŦQ/suS}arE*K&K)дW;kT;"!j^ N4% 1bY0%*juAϟgŶԊjM9LghVgL^[LN-N+Ka2Vx hšx#Z=UXe dybwB",ĪίSIk Ca!SJΧko6B{K*m# aO M# ntW%IO/c"uZeޭ[E{ !{OCzEgp>.yE=04!Ze+Z|E>v[pnq}/X~??Vendstream endobj 101 0 obj 3955 endobj 105 0 obj <> endobj 107 0 obj <> stream xZKSCι >_c;2aLQקUݤ(n# Y]W_UTtE7W/W?KV]|OՊJVwWꊭ"RWuEYek?3kte L3.^~: O s~w <EBﯗ,*)syX !2@âDz? KHݳzYJ/eKfꊳպ\l!SiW2V<2=iVqm+٩- XyvzSϾM%IP= =u/4Fi9@0H'b{ +c> c)] DE&Vc"e04kD@ZʊE4I@V?$k/FTrL  !$kY+E]@G(I*nޚ/3\i_"sq3?;ńߍ]$Iwz$s]K,(c1Zw@̏ Gm1dM{]4Est&=l1fvMݷc9ЀOh" M1D`")X-sh)`p&$nJ '7>pp!bnsGJq́W)%e1eɁ:6jÅ2T' xm$TY3ڔfgyQ ?}NP5h|el]oE I ˢNv$֭#nϋdW0/BRagX?~51W9 %Ak`{>q!cjƁBp"årB}Dς 93^\WQmsglVkn{B=p{:L8Z-ɁMO$]O~ CWD +7׳cjD;^"pw&02 JLB̈́`?9)'\>TxE2] zKGz~0hY#<++qLNN8v0 cIu{;)3g͕*>Sr O2B\LҳM7C9JP"p[Q|+m5E `A9Stw rfp2)zks;Y?E8'] ':ZVEfU^uXK賘-U٫L k$>JujPh쾌r|cJm4U*Yqx(ˎdܜx,`ҘDaYBIF!_~8:#B+G4' k0付"l!(Jc060[ H8z7rvt>uQJy?EN […"ǨK+,ص%IwR1UG P{$.Lh>vR""r6A*P*՗Wj\3II :iO"á="RlhӼ,ۡy HB-Nr}yLFru卵d:ٝ),"p<;Ęq"bWBP(*IA쐢FN f8x ,3 S>'Lq:|&MsmTKc:Emºniی="n`%|l:PVkBtG<cBY8(]uΘ@8*t<'i&zLrUoLC/c*LK8 Rv6N0[ʈ+aXF6xXPEuKk7K.zl8( saw|M`WXsY{o & bÊ\ S\ќ{>Rt_YΏdc8 ^6!l. ^_9NZv9ȠMAV31w# t !OLm#)64 3)AkSi/u<`yzڤ46]!"C,`}d/5Wfۆ]^;l؞NUc"WU_3Ak{d^srvc'bx)lRXw "fle q)J>uG ytK0⢆A%XysHoju=?}"h.&Šen&숳.k = m7@c$V wol W 2f}@`mfwPjG"IߢE-G٤a{ @QYD) ~+(-c[HdDnk&BYʬ0E矟2y>WЕcpNFɝ8}bUyOs\wCuDgpPEy!5G]NY5;cWNO{|@ʗ.y,5KOV8܍&&v1bB^^"7-pp!sC+ {=}'!褿hf߆a>q% Q{Hgr/ tm +PFrT~[w|P~qc}`)V@G6<.M\X2-BtB 'DnB0!Ib: Ϛx< !^=lJjiY%5!>Lekh6fu-_1t?O7~Bendstream endobj 108 0 obj 3774 endobj 109 0 obj <> endobj 111 0 obj <> stream x[K6y6rro^gb$Yds53tK3[|G$1 )ꫯIA7~uCݳyu{u mnm"RWބ7nWǺ{F]oҲLJjBk().2RRݍMv"nF~2<j;(OGyaVF|ׇjlœ\ѸN\28?.]{ư4'xVBK!Y )KkV\x¯Í)nOD4V "@F:,>u0VmD(qLP#o@*ocqOCJq~h5p'4 ah!P5;yQc>LBP}?eܖXTl9JXO!^2tT NCݿp5:W·P Ӹ18ba xQ݇g 8H{<qk& ke~ut!J-W qcv )[*KxKla!b4!?wWDQ4\90Ԇ x- P h`"E߼jp@^Jt)%i@@Z &X9,=vnz%=YP@*l%ymsMQ0~~>^vȒ%!YPH5N_'$Ud|)p 0Fh"d%*H ']>Dَqþ~"k(߬Zn}LtflR96I6I^X}yvZ2>af^'9`)$ǃ~5TCZ Uϖ2Z6 C X9'V3GMen EN`Ul ѥT3'/N;& w<bS T gtU5'0?ǪiU$`SAlx"^_mk9rCP4Tw?m)OƘ(Tju\h44fr!p2DLʽ"9ܚ b>ʾ . ʔA(~樟i<.S&0s- eP "!*$F/ؕ%iJٵ} ,!j!oa:H D䳨f WUAlrȵu51Qt5BItM2 K;_BUm [&{mRyրzl(| j1Gy@Oʽmw}@Y y]l!RP -#$lla1 '(BI~ͨ,&)'"Y )$QYB ZK:/ 5l1 <6j(-A-@[8UaAI30tm4 >ey\PQG 2IYjH5P%yh-@u+6IK6:@F? r(L7P';蚌η=P @4TτM?.P M}/ QXGgڡ-phCצ.xwGm!}%2?jbdM%N%SK#sݍ:ZSyK&R&^n!d;1>s|La2bq^7Ɗik I]6;Wi1-tU `>@5j<2 p6?a8bm6:w!gyH- kFm_O\Zb |^F"3۸‡PjNL1òEY5;qi[G&{Tgnk2Q iTd]j[J#mW~7)**Y' VٲbZl=YHn%ބlrAjȬ f sZd^~ 78+~^<{C6=z "M.<'lxN\~KzMC\;B'&$aj96'i6tq*HL^ԁ,@C=tCKLˤƛ0xRM}Yx\L&T2L"LU28cP0aD^ʜ 5SǪ/ {Z"ұ=iR:/' $/,B^:g鏦I|inUt>9H &X&3'1@RdJ$~Ņպs@M:([%RY"xό/5o DM-א拈T6Z歀םC$i4,Yi jEl2U A$'I{;b%@oDuƂ3pTmDLhR͎#09^m7{`9Mӄ|jSBp[PBSh4МsTtcA+#%[ b>ҋ\i [YbI}w}r-q.gSM2j.|5 #Z)/]6DOz=+œȇM}D7mzr'W#qųCSm-S.nZ/=|j',eRB 6: Vpi19Jۛ$Q3 u5P$f[HWInؑRyzx+G\j.)8ƭO.[od|6^S1Ѯ(+X9WvzҶJyf9Y)RWt˵{R '$]M( |zS w}Pj#U mر: 퐺ٱ}/^' brdDKY_%ރkQ#k'D+7"Pјoܯ脉OoS.e8]:&wG*6S>Av"}i$9@͏1|P7p;Wڱ!3|DZ 0ׄBF87d.T-bS+m!y~LY4^{ 2 2<УVN6 )JUGS;E:g' 2ۨ! TIྯcVRLַ[p%/X#ƱǫDao,$iif*qI8N \»7AYǗB<%^yMH4Ч$Q}P,?]- nendstream endobj 112 0 obj 4140 endobj 116 0 obj <> endobj 118 0 obj <> stream x[KsΙ?G\răf^Vһ(zf%ەȵ|=+].~}EzT(lusw~@WVWlZ.^W7JqxyxhCF*4nMJkۮc2*H/ܳ?K2Z&> Tte5ԏ!30V&,!mW¬҈oT⻯3&E[uu0Е"_0]P\d}wi%Po/TĚ+*YoCd+ԙDTehwO~/n^!vǴ6pm],1s(Ǽ>]XQ6ۍ}5/t'ְ3AZçxJdnN+,.$=,x9t|0qf8nkr >Ǻw͡}޿ -Rsoq ͝)ӣ㋙A sg6WM@u%FhGR\;ݩP0vU)C!a$=/ve:bJ碲)4f_?tAs9vŭ `+ԺN?e_rUrŤg_AvPH in\)lۏKKr ҋ )]1@,g!EM͢&3Hs} DP n!|lUSq_?Io_WTRfԼۿkz62ǘKIG C DLĚ@/*e/[0Wh ,Ѳ9.[D Q8-~D) &`YnL~r6S2uA\K4C;De6 $029dwA=V9'+Un'Vd*XQVCjs-wyU!QQu PѦ郸i!J.tAD‹AORd^ N2^F5~E"MRc.!p1N E@Ib!C+ r4ɺr7M8JQgοu!d.PG=O:4:m-Ij" Aq;cC~>>޾ 1q08T3n ysE(9~>o?WY!).v:aj|.1&n~Z !2@\\Hd,;Ti FsFӥ:ߢ\^gtC퇀M RJI2)DXpXf%Ҭ + B< X10;}\`1G_5S>纬U 2?.m8I8_U]\ٚqW-qKE'N_gckA!cc>&ßT*>gHu/Uh:wP;SqjeUeLDOD?ie%i?Gy5_+|(dhT}CN0*=:tG:- "$" Ij)]""Ŋ쀧>'ɨӔu2bec Rè`CbS/LYH$6m}%AJݦ;7ق2[+Ipū r4,h ӦBeTz󁬃FdHsD.ՆxiXOSuPNVD\C^.*+Z-Ĭ%VR1`u|FXCԻ1@,^xj ,_`I K#mv]c"Syj_Xvx\KwY&L=ToΡ_vg.KY~`R9RpEFƻwj;'bXq٩SGj9&I֙ b^_xO:X!*d]:qJL!D :kzgVq>Xqi91j'`tYU2B(U.ܺ@ط A7!2oNaY?.abEhZ%f0'幅vӰfΐϸ4@WשhVݵMUA ʷ*Q>^Hd%LgW2sB3kJV,]XZ|.6Vs|1enO!h̍6 2tzJLAw& حҁIz+$ʑ)z患c *ZЕp?HURí-"vdƝ,mĸ-H+7I{.&$ɅԦG\h4v,g\5MuJQ5brmv\ӇxO$6= NeY-^.<&,_{5T1n ao 9uEIsvE!1d#!aynDU0Mܵ'TvG>w%֖Ę当ga`d^$aaIZ]]9{p8pDžu˺iW*/QsȢI}P-*W~ #N(~?p,endstream endobj 119 0 obj 4437 endobj 123 0 obj <> endobj 125 0 obj <> stream x[K6{#F W!>ʇ6Zp#Q)řm@ !-\šF$ !__oo~}o(mzcun"R7h#?IW?}?k\6H^0N{Tmjfl#˷{0aF󏙆4Xq o}\5(ǃ{yvtk墑q{q*,U+Iaq~8Tu"$llߺWLC5,<:!j)H|&(Q/L7[+u7|l?_mb/p1J8`4CHb}cFl c%(>.=RVsu(%PP Pj&S7tc *06& ݙlomhЋ&til~]h\8["w϶n{ڵGЍ,s t۩ ʿ8k_.BxT?g֝_Bdwn8hYE+t8>d ݭvCξ<8J}U-2ywN xs#x]-౤Ƈ!b\焻saD7ĭ̒!1/HCbaˡD@wv-ʛ&Z ?çQx3!S^suxWrKHbw}lV1e3^Yrbᮮ(^&)PT}$ DN\I, R?JlUtM5+P\*64G0 IK%MȌW*{wi%l1}8 X"uRω:p2Q n]; ܶhK䁠xjQR fa+Ca\ ߈4t2fp+ "wr%w*H?aPVՀP/Rٻaj1`t[ͿDMٛn:y&];I;XX4,qs=@>VH4 "5ȞuLJpER!1>MTey$pT@ʻ(*,@2i¤z5mw2Ӳz /6}7 K18stkk(,t\Q:q|?Djې;N**p䞩'ŵsR1`WW6_%:`;Z pTF ;vƋ$& K)hBn,g<дB_}ȯ/c8>+\->a]̼W/)s垂󞋮ϕx<6WWASTjCVJgƢLoR&eU#rsJm-sbsg}Dogʼ)%h( oSz:hF%ލg: m$) dK@N0=3 !O,ڰJO,ָ1t?<&1c q{}rob-W6ի:N%CC耞>%jBc/XqkX4И*2Zrxa(i4_cE4Vqѫ+ Ɔvw$ȖfVrzA| &Mw߽zߋ⇮=sM)~)/Ohɵ Ei2B,Ly Gd1҈7Y33I b^& ]UUǝm`SYdVfI}kw9>jQo](m!܅X" qCnp,~8@XeZml S֝iG7o&ulwH.{MM@KScv)&RńUbS`p ql??ydXSC],,ᴂ?@]ߥYE2>0Q^"q$Ki%36&Hjjsqe\]0LF~^y!ҥtG HO6y{_!p3'\F*3Y 7wqq֢4Ǫl3Mʽ1 q)28K gomt0?l},B~=G/~S.PbVb-?Y8\^u.68uwjLecZY<]}Qꌟnny9 kk7\ X{Wr;> ?%K}$!n)€&[4QNW/~8-^v61 Tfe ) Js W ڳLԹ6jf}jq13yfս%Y]&'#*&`0UdR7>D[?.Za;LVger*V>R׆ KXe^O/~z?Q6endstream endobj 126 0 obj 4464 endobj 130 0 obj <> endobj 132 0 obj <> stream xUnP+Lvzߏub@!1m*SۡWPd眙%>CI=mvV\~Xٶmlv_p1Qe1in Ȉwnt H²=™LPQ`ںޅꊂQ"k ?]ׯ|@%DWݺ^8b9kZ 0RefMmu[BHT;?nS_M@?ꅠ4+` Q*^4!*H;}aN{ ,E.s]k!m7.·g'b(IuQ;/7g@y&t@E<2wݴKFd4C$@+Gd OVU}:|hhkUga'ċM|_LdAzBmɉG=^똌xpg0AqMGx~Æ>:7v) E5ׅ` QG["Qц[Rf`wk®Zg Xlf}c,RMkuu5F! V[>< 3t宼r˼3pU jlFVK y,R0nכM`bAjqd.o<6l&C[&tV)^416(av]W 5IYySWendstream endobj 133 0 obj 743 endobj 134 0 obj <> endobj 136 0 obj <> stream xYnF}#F%ozP2:4cYϧ]MPc|iԩ%J؆w _~׿!) hJ\/_];BS0zwv$:6D(˟\^ic$#?ôƢi0 DHuSd+4qzZ SaPj4IIT ~d:X0EkrMtM'( N)7e.qLdR,F2>ꢩǸp.!B:jX8RSUg`wtrD9LzW֜9(In})<3E! x}Xk$Jb ypS J#CD`Dˌk棘 8@DZ.&Uh &F>zE0pl^ȑ/RzF(B"\  |"Žv Pz!I@SP1l8ooEE( |P%7b,쪇Z[B[L"RuMKPVo 3áC0_ci3O!E,΃uBIW?w IdB]?՘o]U6.qbv lH!3`.%l!<󪽪ob8՚u;nҬ{j>XW-[>(ɗA( '9cCPK tޥt7-QrTB&|qiQVَdsU99+!2*2F߀mb_7uGg [ @I=>nH VTo%CӽCI_BN>SJc*c6ʦQALe u { k.X:F" _yelפtjMa֖W2Qw̺)-39y=Omu/%!q ʾ+m*@_n}aަ!TuG%T{Ud`#tg ,X9'O-F!E@k~u=#}O5dy IX}H6fo@5"?m 6̊ 8b%ۮݾӶPUM1͟=>S,J<7wΘHSL"ApJ`|;5B 6+|?胼dJyCT˜^JzMf2A 2FPgA4Ovh s(yөƺT5D %t0QP1tx۬H4An (w 55¹!Rc,w .3y a/R sΛBUq4@!tHX5= O49 H8@ΏYKBՇ7烧[XʕԊ'\.εsRڍhH|*̦7xi@|*ǮX4s@5>r"2ܩW=*̣jţ1tiN{K]V" CׇGĨ+{O䵗=]0{*yŔSMǴuk: PoN<;e`U;"e¨ȧG> endobj 140 0 obj <> stream xVKoF W(4÷vўCn'Ь`2˩A܇KT0 9K!IυF ~oe8;cf]n޶qwc?o !O;X"iDìNCq<Qxߍ"E.D[9'iZ/JK2Ub.3EDr@.x[ck a#j?5&dq,K{<ƺ~q^ekl*\0ATTiĘ0Pu~,B & >n*2$(54]U36cIz6fdד}6KYĘ[v}[C<')-$l ,~wZ"=}XϡV$4ݜ M2Ђp1zp:NP3Ba OOWs)⳹S /ԔSߖ SḙM1E~~}iP: aq.x/KL&i` ))0Cth_sz[,f槂l -/M *A;h>7rY. K8Ai>/3}\5y@ׯftlaQ bI^ӶDP3+R~fg:Cf.\tXUh`I]v[ |endstream endobj 141 0 obj 1012 endobj 142 0 obj <> endobj 144 0 obj <> stream xW[Fs~*u/yD-}hʛx^lg?3㙱N%̹~;g0" 0y;y' ?Cx9` !A@H$FR&ajfr&Hdp1+"qEiϦDُJ#Nбj6V-Ǫ)KUSfj;0hRNᚳC'SA_:/o*OuaP &Cd$A#'uYU>mNcEC;L"cQ$kFU±\{[\/`X`BBB^9Z e3֏\дJQ663R|q^ZB)2d, t/ yG=@gQU?*z^f׭ :"O f KRBs < g/(O@ĸt>U K^ 8 5RT u(0$\'@{aYMYo="9Sxϟ8'D ]մ!Ck !ۻ !<^}hmp(kyl1A:3/V\@ d`dúh8w]w[B04x2efݝdԎ:):3떣g\EʯaAufA]z*<-1Uŏ˓`Yv!Iΐ_KFZPgXAT.PlLpa3%>҅SǪ%xȑ\L (f\fTW TYqg04]ŊnʖakСI凬rHuU *f6c+ǒUd*UL媯=X7km`?EϞs`0o`TfGh}[uVhpv}8BȐ! ?~ZM˴]W2/ysIə{&eCiX!NJ7Ӳ;o(]d*`}u_{dS/+ҫ}K ȂpܡG"BLp7O>ܢ#Ep(7Yz4`/ʍa!;xP rcYfa9n>[ֳ.S^ Kr_n49C?S:(p~벡RQ{|hzIsnC8} z>Yo{ٍuX[h䵬Fq)Spg7$*bх-݄f%ߤu}]q,gt8x!#alq퉙a(Sw{c5-*kcpmUZـ ęwt0@,FKrпi,_o2}endstream endobj 145 0 obj 1566 endobj 146 0 obj <> endobj 148 0 obj <> stream xXMoF :"z?|]0`@SX"~A o޼7k۟;>>ω[l9;|N8b\r=QΕHr;{Eci$R+ز\^7Ev]P$(a=K!bB5])m.FL*.9'!\j % vSxXЂ"\+zErHcM &I)nRʸpD"NEݑↄb6 k (թDSh; i*¤Û)-u[vuA,VO`" #!rQ82:!7RV!&4ު3}H)r٩'#( * vuW]{r]Vd0SjˮNܹU.m1lE5*994: 1&Sʻ66UԎP7f#t5&2*^5|JARoxJ"gC6t#`i 8{y " 퇲F:[S$HgeB^WgKO غѾK L'&t}=J]Nm:|(FSi ^&aMof%ltv3\3ے ?;) 1C$R`"Oad/b|-i]H~ S iʻ]CTJ V|,M]뷎`jEChxȣ<`3.Ta#6ɵ]gBZMsu`v qy:ĆI&=˜*yR4mYWD-ӗ/$?ra*_sB|md1f4mt~סXj1THzA]כMWǗBRTXXX)ַB) |]uU@O<`r<~ L.5:n >T]=%/noLBAzY~ 1$>3N@čT x L&b0:F:AdEoe FElIb;Z`fU^ xUGw6mp5tB N$XMXcy>SHhP?E w\cibۂ `ESendstream endobj 149 0 obj 1596 endobj 150 0 obj <> endobj 152 0 obj <> stream xZMܶ#|\7J*Vrlg} 5e4CIe> 9YaUݯ_n,ئx/?ۛo~bX8Sex_F)`śs% +,0 lHF0X`}]IqM['gr +f]!ø4}PJ`<))J.h Ly[u\Ktcn )Q1|ҏ}nYۺp_Fһu?%/IfÆy!av&Y?]݅,UK_!`YGꫯ&<EQ!jfܙ+4O [aulDzioݽ$ 3VwHץS9RxHҜ ?8,w}E$2t8]['_4-/sSnv#(" ͗&rv: v}uP2AJ IIsœr^HFC5$B~%J Sb"bc pʪw# 4ti!3nd|:%q&NCƄ)f!ː 2ifi/ҝ|2Z -]?:&Jܒf݃Ga_%Cj45JpBN >&.$ԬW b5Ƈ$Q=] 1B|E t1ݜD~ ic|BG7$0O`z%`QFAM(1pB|z`Qchx[B)Ikb|9.p9eu6)TR04a)tr:dcIR|%+8w <#oZbP-p9BC N1V`xp#eiS&nYrj1GB(l+2#{SyrѨP#RDj]g}${"s#sN jvߋC{MiE%QaugOYKQ;BˋՎ#NO'y!E+"RIVzXH$ ELN ʨ&c)g%e(cN]P *ȶU_ʴg=cHL| W{71M!Enb:eFR)U# KiI~P@^wcc5jzO͡PKMdjc2W ].H`Zkbˑ9V#!xkז+uL۝ja+,3Slog <>#!?7y|Zױn-:/? 2Xx "@iRV+)aPCQZT2Q>o.޴of~&Y雩X2A WSr2!4UQjYPuq&X/ r>jQYf뛤2$9 Ҋ7/wy\35]Au߰|;g%:}yBKmÀ@6We'fU*K^%Up95n{ћmcr`hyH,(ϓtռ3 6Wy}}HZehad$y˳ljxYQt;Q>3ʼn> ~V!3(M"䏡|jt?rZ3pN9ѩdn`Ẃ]4g.M<6-^DD_Xx6עT+Nyh~ŇiM׵i ))]؋%3q s/@dbeAOc1vf]퉆 ˭N;b7@ ބSCq;ùm~ YE1sq,*ˬ0;%LR0B[]wxRߪYz7*T)E8|a{nwd %dh`nmҒW>}o - L_dU0#}p>#1Es/&lI ԡ[o}SMen5)O0^jC>ۭ$Ȑ^R_:̽{c`$4>~\05p_)/0I!E x Dn!ä執o~?]endstream endobj 153 0 obj 3658 endobj 154 0 obj <> endobj 156 0 obj <> stream xUKO@,VHh.` x_y4=Tr2o&9Y>~wN^3lZgUdY&,*'Jez2?0iL'u dz}p]Ò\)aCrPȰ<jWcCRp cD\]vK0Ţ-2A02l\(M3lˡ%(Mp7C4Fx; Ma(R@4-2\Gh& k41 ]P g!T+eqk&r݄V7IE6oQ(,m`*]PvDY%"c  ':rP(fA>Q$B\_\ޠJ_*i/>w'S|xwA@ k!("2*QY]3ЀH^ WNr ^vgӅq$7Qu0իʍ j!?:3H< H$ jrA!hvl_Ҫӏ&ɷ1"v'p/'?"l>LӠv4sKw& v$~1t\ȾMM~Ӏendstream endobj 157 0 obj 793 endobj 158 0 obj <> endobj 160 0 obj <> stream xZM#rpv<leWeUNFrb~iHjKyr++9\B ~uSUɊ׳gqQc= l3cE;sR\~--TBL]3{7Pޜc.}iQăngVVpWU Y,n׳?yuٟoapfaK*o=a^ߜ4'\nV/U)>9YrSK#Y?lwMW˻Ww/P,滯^]+Uqww<.CXUʱ+6̆XǮ?RR)JvW9+Cdy)+K.KW~~+JU)෨ju\8}pud,ɘCV&r71LёU@Խ̔\ie<:SJH {.6:JH-=KU)\#6*7,M7єvX:7t% ּIWJ L)%@pUqq  x鬱gM)Fd)^n7)6CCw N#)Hn^x!𧮎hsaqBWAb{4OsKNptغٔO*oJq[ (ûf0X' P ~d_.E"Mnb Kp Q`4ˁo\ԑAmn}7q-$c.z%N\4@y($~$5Oiʪrɺ$]U-s#OV|OoA~{(Iz$!DJ__ِ|tCGHuڏl~<|Q9+Ǡ ӷmТJ{. 9?:'^|<d<&WǢC 33Jܖo#0Ʀ c$z2Ew y^ﮒrFEroF mZ~Õp; 'Wxֲ(Xޑn}V !S3zF`ǔ;Stun8}V~k)4'ͅHp'+T(h/6Un..Y:x.ȓQ:Q*֫؈yE]lT @Ly8O7H8L~Iwo> endobj 174 0 obj <> stream xY˒3^%RE~\(W6i=3L2A&{4MJFǹ r&忋ϙ7Ilfژy>ߓ??̶3Ѭf`h>['3V3=gJUsfog?74nf_(kI7wdhZ̞۞^Ϳfi~3eNc+M=oD`1my;qC x|谰 80!uﳻO˗/wrw~Sw32b 3t9l:>]8+Fs*SHמiz8Oy[(v@*h%A4ݡEnn>*s o%N9}8œ NYUB }ĕ- [qr60.ʖ5B{z#<MD\ |: ȵ 7]i6|W`ôzow2/˸8@XWֿQ1 qa5+]PvIXf5YuJ eϸЦW8OޞRā2JNyOC3VU:1(P}f DmxE9O}c}h%l vۮ?b3RRe\RN2,oB(gN,onGǨ`G!k !Y2V]gn ccHN*raQLV9(nSwk]湈 dž2%fk0RM /fsnUnw>ωh6Or YN؄AOnsiZI.H'_mnM![S%LGoҨI|F Сn}j㪴sÎЍ /nGBP !3~[ I5@9W4pN'mKpqI,-+QWJlE1*qiqFX||d7װ jrú- af+ka VK^vLNck-]p*q:#2QrOQs[nD,&7 1EǢ9/)͍^.^NvKђ:=|A@&B؁: 0*CTSVҟW^UqBUl~1EWLC}hشqQj% <.BݢSMrb/!rI%0cLiT*ΐŷ!ъEQޯE&\ye $j\mrsu>]DYRb+D%s_WBnA)c&xVՆtš:AEcG<8!%, fxSޞ)GTc )]S}W]Z+1IjpjGA9V"J"XtI-4 ?,~ZS]`n\qLr_\Ve^&)h $G)-e$r vON~5HC"W&:~=yGݧ3yIӪ&b 0dʹߔ1;DŽ,q]ި =椽`s<1ѸF R6.Ա-9ԛ5uUieaz?jkv:&Bqa;-(h;:=bvwcr*jysbĨs%}=R\BAx5xO "6;+O\(d!n *V*Vr,{G f~Q*&nHX+誠y",T}$xG)2/$`UGdyMa(\6JkkjK7/?endstream endobj 175 0 obj 2646 endobj 180 0 obj <> endobj 182 0 obj <> stream xYIG[|} @!1e.f:ݦ=@~}^m q}L R'L>L>LYX-6ӿ2FR|:_M l S)z:LD< /}d)rrj]jF4S:=jm3 a'DX {8;o𴺨aQ#H7Wnhm^y(7k"DI-ku:_j xk wQ1+KNq6=TC| HjC}2Ikt_H#,Fپ˜8+s(5^|b]XfJ"D ů8zvEA gBo`@L2Hf]Gp:>c\sW~L1h\[%G`ƍ&LXmK@> pq74tզR85o h ʰb2cj^%<"sQJ /ؽ-QJ:v1%1 _`;h|a[4&̯$lUϜ_F?M\FC79 KbBpA]XDg:_А1 V#=\e0"# 2`FWX!6ds&-p(x/[)vuu_gd!vĨdĕ^thm#J+l}0U: >H$y{TDW:쉂㓡zqg2c /:%*>1<ȯb2S`݁b~z>2~3<tfRm7ԥ})[Wېȁ u4G rMxhCXO@DV`9L׹q4Er뀂I@O&Rf Ԗ#\U^Y(c`  h7EDk⪀ ("P.f%Lھo%=D+^/^** +TP$9~g}=RTf Mݏ󤶀yznX}W-2PjxT#B_^L1   c|fܛ (}bǺZEhF 967:U*MG3YFХЅ.q0tv]jP8ima@'$B`asJEu=TStuGy=MU<`.*eSݥ)Q1Aݔ;q E*+|^%5#ƱV 3$ ;bfv)̊2hKå/[<"PJH|;((< W`Vx+ 2'fLkeƲcdhMAtXk"X^P2`D:KWrdb-:5$wgs#hPw"3ID yVkwd(mO|@fVѓqƪhm- Gm17W|=r v&DBP ZP* Pp0eih "*!A`5rCݶCZ3z.Q6@]96rС,swsf6L}82\&~ۭ?P$|i(9"SyԄm|\NSn]hsn)lvzw? TXTP%QZBŠH ?XB&lL=>xxEM}nH"Tte>ԟXʀ&(Id0iG5);oX쒲C3{Hxά_c>}3y3wendstream endobj 183 0 obj 1963 endobj 184 0 obj <> endobj 186 0 obj <> stream xZKo t#drr񅞡$zr$+>կ&#F{E! $zwԯֻ.^J./ ]jE.v/nWW_.o\w-s؉rc8I%XۺgíqKqiwwGOdŦ{6~6 -RkaԲĦq}YID2^|ism60ɴyU?E'Kx>넶&pۯYcHAuKBDҮk>D+p|C劊F?,6nGe# zkmM0!0TmsTK)>׷ >660R@ا;FZÒn d8CxW6yxXLZk!V@6tk7R`ČQw}0zgwzJ!s8Eׇf8DZ{@Z4dMl4e"C4(l 0Up0)jSǾ)S'a!fm$H獐&1V @đ7q#2)HW|r !tZ#}=kH)X?$e ^O[^54+Sw9dRNZnO %Nuw~Y$4^8YCRrVRi49G+iDJ$e]!`fEe2 /|i%e*( K5̉ |N\PQg59f".0^#.,d"0, :9fJPP[ӈ{F-qohv p-8@%Iιn԰1O+ 8o8XQi/Wп04d)UC _#J5&u#H ?ΚCtE&83$/w gsCdYbsY%[]ż퇘 y>IjJn5;H4unקm;rv QVrۅxʤib9@fFg̟ܲ#nZ 9SAJ{2;^j=QE|$5AFӕbOPڢB۵,LYє^6ЬsI6 AWTh,%.GoW PZ>-ml!ނ3\K CJ/p9Mn ,A6g:>_UiJR-n0ܠ߃+ -]0+o*\?)7)Ȕ ;Ǣ`@dħ\TyQ~n^;G=NIgh#ZZvlmk T Ǔ1msq/B,=摅AN@(HwC O:rQtFrrU/S pݤoL{gb2R嬎B*TA DS+'TU%"&$YG*%T)'%b$ɹo|{U30LDU]h7 хpzl"!\oQ|*(XCy"xS_!yWa1!'Yy&䡀_JB!IEyZ ږž11ַQL.1E :syM_+F|0.ڕ'Q*Ӄ7t(M þܕӋ[TSN{Tߥ&޽uȤ9*MȌ }B㉇>!s6* AhG*젻x l9kbPJ xڦLQ5SbYyd23wKe߱8"^u}5$U"VLd"=aŃ8HDq9S Ռ(*0ԆLO/_|}}0( 䃖$mһ4o|zyq͛#M?3IBvc",R Q05j.\Q`D$G Aʘc|$)9'0M%^sy'eQfǺY[ۿ݌f2PsO?9MJ҄M)~XSRg;uDxJ7"_m@%dDT> iMao˒mIPϏf_`EƷ5Zq!&| ~^j+&2őed 䡚H\Ka= g1u I 7YU_b "k4+*AUpI 5jsw&Cucrٺ9d\|/Ȱ=Ҟؓ)kz;6׊2ӛʛ`xI|f?B o܀awo8(ֈX/\Z<,FڞL=. Ah9߅銀༸!-~|WhP) $Q+y9C(~kLrj<Oߛ& ,ǎQ c1WV?$Il>ƅZq_^Iy $BL߯.> endobj 190 0 obj <> stream xYɒF3:f~G;B,׾f;BtAݰIڲ $2ч(UY/_LR횅owX]}kƈS[;ChJ^oD|c%a+xi{Ծ=GGCw ~+8F7y&:NWۥ݄&+i/1Mkm,nk֯rE$c5+6},3i." D6.:7IqSN/ݡ VHA_rwsa]pܔy,UqQԹ욿{`l2'9"(/~1`5 ^Tvlvaby}w|L0ݵtS36 (mG)ڈkN 3XKœtC\raf'bXd\u{%h+HGt16#jc㊿v:#|!B T ?`\/=O1rD8KHstHTAH犰]+Wo> la#Ѣݷ0-y"+9 %zPhGVrxH 20$:arE4c3dt?pn]eIh,.lBiQգ`Ԑ谔"\c{M#ՙqhbm,Cj>1}%QURG۴ ?R2M0`[H\|l]08eAnbVgep.#݂b4`)C,jWGWɕi -HǘS^KA3=ȳ`"=W`o} #ZR%rM xhP1ɧ鰨E$O#E_ݧ=AsDJwK9sN1VQDX EXAf}F7]MҰ (6/0 e:㹅_HS򡝦M-cU]l/Q@M]?O}hw%EI8nmLY ("M0mI"s"E#/9C;5%ۦYڞO™jۏ67Z0y;Qkƒ) H-&1~E@Dh>vjt6.ITuNG`}z%J5TKS >5ri8 g"4.`YVq&nʝɄ i +ӣ>ܖ]IWѮ\Rz5bM/Rok%~(o227@yRXE8Xߓnґp !d6P p4Sqd "m-_[AJ(SqIw6xdAx/sAsA3 ."2S4D'│?>!!F}研 &d1Tn_(RD= ͢| 59g|RRUVk$\i#4T[1c֖;˩_:A=%䫾%#)? <_$W x;+Ҿc9X.D̪ʅ26M>`mß|P8~5)x)! &pv9п!ٟ-\oZy~:r"Xq0YϤhg|]AȈ> Spb89SvLQ<߯"3?&[]! e+5)?up3UnNX._]=ǤwMY ._1mKrĩv578endstream endobj 191 0 obj 2331 endobj 192 0 obj <> endobj 194 0 obj <> stream xWrG o\ez_|LUXE|Q-jR3C [t F %_v _7{)*ƈSWU*gDjӭnQD0sj\~8!0Puh"~1\XԷCxri`|]LЩќG=6u 1"8hfο4%@;{ꦭZ]ԄJl"тS9NC·mCc(YE(Ϗ (t912C&wK!U$MnjHjoC!#]ΧpR`B]9#L ØC.i~ o-aT/P)G%_^XbOS `a2?uNcW^k!C2S-+w}zKvvxH؜a}[/&bm@ġ¥ m|"eP%™!O0r쨧-;\p$xcD(F)? Dџ` DEf VQ8_@C0l;s;$WItO)G|)u/%(6m)ߠK-{L"s)CV.0b-6Y`cxjNWpɗm*+?~J+z:𠁥+kRX HޞmB[9)@<s+Pn9ȕK\WpiCN(%0xiRV\Zmz_ya]V ֞86^H!lv1O*dadsW b 6H7L1qKti~heTʏ&%⚔vB3 k`ub 6 gWmOOIendstream endobj 195 0 obj 1234 endobj 196 0 obj <> endobj 198 0 obj <> stream xYMsRi]139SælGI%$$(v׿>o rS{v0=ݯ_[_z늅c_滟_f Δ\oorZ]Z7(Spya+ m!iʇ}6;FrUϻf] ͡+SU}>² *Lպnal\r'wh+& !5Fa0EidY4әBҤB)_ġcشO+ \,&es+j43l&ttՇYx^p 0ij'[q6*m8l{oR[݅&!D^P@VuD$σCմ}ŸU4w7)8]H!pC#X`y%˹> q ca _$_;c&A!AÛ>&3ic KD_JjDqww2#yIW|hCZ;c;[c| K9)spQjDt 92+=?^H2*m]S·gQT qWѳUQ&Bu$K7E)H HmGUs]$ \ޗ!/AQ$1 Sbrl\b JPz!ij6W7L5Ԅ\^mZe(d eL %_6]/x@KĆ*c2x+Z=\ JVƈH++~b/EV}kׁq!hőasw>Dc)9<%%mx9*ճ_T$~5) *ъۉh%(X*ñdž^| *"#.R@BSEL 9{e$TyL ]-ao zxcʙMʼ{'h r38uɛf˳"y9RNQ㠼 SE(C(APwl۔K(a. yt,%+u٩G)dg\| 1:7MS`*0Ѫ:1{hD$ }-pl(M}E;FAln'gP'}ZW.Fa(GOv: m!.$/mo(ҝg)[\W}sͼM}5U|VAE;Cn"nHb\ X슒[4.c#Fp5ԥ2tZj6%mhmݤs x9òQv}Z6OQvl[??U>3scv x˝=3`d6 Ҧ\}`&y9rʹY4kFNq"0y:2L%u <6ʭ0uS#03a+,#'ðH1i(Kzr>q4ʾoDBP:)O!:^ R 8!e.7=&5qB5Cyx2S~iICYtޫOAR|G&:W8K_j`xG44d58of^(AZ+Y! yk%Y>5/nM>iu~/h/eh^XwJB#r+|MPI8YnSJb.rzbYYՓk\@:gZ9Icp܇cQs9/)#,{}@f<l9-cN+ 1'2dK : aX"4} (2=|ugVl)^Zn&r]HxpJ O> endobj 205 0 obj <> stream xYMo7 s*#Y 1 9H"tiϴΎίb,vpF9"Y+_yҟǛI"櫛[9ChJ^<\\J¿O|>_hb(5}ӵc0Yc)5]k>BJFk$.Nh…:yc5\_$ A q`cnƦky?|sm*[lu_͎Ư7c\\0D0Y2Etܸ\O qna|uXsM뻇iBy 8Iuiq4e2pzy2Iqu{ ڹէ;DQǒjlKYz6R%YNmOYK.b9@tm Uis0-7>]D2,uHCrXLFy)L}쫱\(*86U),żS̕0i}7 j$[RNfisbvd#b9C(p.:J)glq792?vLE1J*da- { Xdڤv>nr|5M?]ʼnZ[v&+w`ʔdԥ!NC#>LnAVLk)b:2<]|8LMGBb'yX+"F@TSǣ3d^@0Q$ hb 2a _ Z 5E/>jt\%@T N MDM$OW 'ʰN`|Ǫ)[f8ֶî J\ΒsPY: iSaP姫!{גBqjN'+LV)H4|*`,^h쥼r\:N >qR,#_0Kl2.xY_rEiJdux1s7U")-Yeb!,ط]<#m5H^I :Q7CAo! b2X@K[Euë,₡Gn{e<y QCEe@pA .붾1V?h0_80 A F  xVGO֡]Нq> ,GMe!YPDKF.XjE׶ fՈ ԱC}QGKiK}&mk[j$=RA‚zm#sR@oAd\kbm WeQl GhBt5/`=?WF zFynm=4}q!e@xmݻ OÒ]bKiT%5H-!dD-FAssY/ZWH:67 SM[>'2 j%7,M8%!$ɇ8u 'p/:!^Dea5;owaa9VJ7WfWa*QsuFKH%IQn\bK S/n~y便 C|frgS}tc5tC'9b[iJ -GI7ecWӖV6E q6PŮH͠sx@Y\2Bk/vaJmXVBRN/:B6gY?꜡c[sCChn`[h Cuuܢt~8A1J>ܔxFmVMhI/{틷JG Xb{mms_o #^2K@GasZ1¸NA+9۸D@b D|E5=}ro>bno;55yYGs,|5ZGi\yMHzo^h(0[x SSŒ γ> j֨Ӣ#,y<8~,B#7GʼM5P*NOD!xD#pPIةA/R bx[EH|Ye X$/YEZt%RWF , vŝ-d%I&p~zO7q|.THE0&GhIoC Z'eut4ǫ>O' 9j(U} 1 볾gF}k@]M?I> endobj 209 0 obj <> stream xYKsܸYo[*!ހ+<6ڧ6B@yXV~6F#9=xQlU뿥ֻ_n+& !5__3_]ZnwWd!_',/pЅ)K\ӷ+^1V8i5wWR1Z*ʂsSB9mqa/km~5O-~. ߅-i?R@֯ '6?ad9U2r&+) 2t(|x~Շ)V?bq1YzɒO#RX.Y>5M=mcH,] \5u~Iu:X5w8 KUĞX«pPNF:Fmb.sRl]T1?9u~%8`s%V'u,ՙn__+8S)ffnSJ h 3Lujgbw]bev`])iXS:;bɹwC(;! 5s-t do֐_JA)&X%2]uzvunCuX59F LoJS|js1b GQl EJM?(#2:gNQVaI<,&1} w+E 3LO3qMiw8f+G; j|P5EpM~§kk1l6`t_3`UJ&%,JKLBr)-iqbT>VqaB 4%GZYI3EHL;J50&P#"WԹ̫ݴfwK.Cg )xl„ԃ0yٷk 0AŏRK]3D 4z$ O5*43H$qѻG2wtXV tlP"Ac*-r\Ƨ +O/.YCe6MJDӀ·BrmҸ<#:0Apy *.uO1BM$-h/Gʂ(i2HI=܄XH: 2:y1@)=.eMN,F!8UIB ^9?#羒F}$)"iaMT Ք]E@ ,+Ą\AiaoJ[B3WswW@n6;]6_h X"F,HIh+I2%Yhܦ#|; |.1 ,^qz~㻦^zWj$tWj8# zzY xht)<>-M kii74*蹤Z9rj2zy GdbVÍO:v2k Wf9?c{nR".=} 5E8WԼ7$4 ҈ȷ齆zv.@փRQu[SQ*t]X9UK`&?H9i]u'> endobj 213 0 obj <> stream xYKs>ʏؓhG~R9XotlxK"]aQʯOϫJҁ*`nPVK7WW,<6շwW>0b8nw_`+g_M֫us~s+Ɖa)Q#Msyݗ*#L o &sXDH~K L7ԥ']Sok6M]{v3*FM99dN,e.! VMo1z7ĺzSgAz VؒQB ;z-l;KvDjG7>"R_s>ZYx xUpʊ=(,^Tuա.rPmC(zXYXby9]qmCD}o4,K20K$&w0-aS ΡMUIfqcK-` Am9Oxlܺ]@J?TPHZj0GthǴ{zl6ŝپǘ2.(1`e8>%:@H ܇LLˡ{ lVOTp3Th6b%vfk)Xse LWC [B(v6]sWǼl7!8JDBS||77^9 芎M?ò]4r6[=)p϶cvyZknR|76pTcj&WDDk.\ysDBВX2$v!䲑BN\VED$~х RwjS$L|&9QJgӾ \EvGI'1U'K5BS,p@ w0& {1֩h"@Ic`1ȹDVC>"mͳ8z`pB0SBLj>5**W(AXixh)I-HH |JB8$z +g?\w?z'`*Rͼeq =al@#ǔҗ7OU4)nY8$<P~(4 P 0sۭx v>ǤP[-ϻ =i7#S8AnG=x2/cJϯ Hw6D ފ@.1\WP>.᜺[4^쓍MIBc,ÉvE]EeеSY Xmx=^2trz0P 3e!+1sX"(A,c:pX~<̐'Gf\ׇcCg"`-ӚCڝˢh34&bޗJqRH,gFc sUΔgǭ!RmGXN\2\lͰGБf7<5XO)1tJXt~d|0\'Dy6}J#]tc,XpQ#1cb~uДvݧ 5p}t$OU[<@)AIGQLrW_G"H3/: ^" 9l0pX洨aZg'n 6"`p0fa#OZEe*~H43.$5 DZֻ*y@PW;$bI?NX!4?VAƂKnL"x)18i2Pͱ8 <$I1D,J=sC0DrИ @n"FAЂm8gǔۊGʚendstream endobj 214 0 obj 2450 endobj 215 0 obj <> endobj 217 0 obj <> stream xZ[o~#ڀ4( $n$V>ޥ$˕̜!wkMpsƔKſNjl\KVoo//Kg4/o$D^zd)ūG Rᆺ7ՇM}S}if^ݽJ#Ǻt. ')QG??%MZkCuAe.}th^8aߴieezz]W0JVVCדpy;hZ&2mw a%'/i…:.}m*q_! qd w$S1()mOͺpS"R6~ "q SԹtd$Eoj2C"À㸅 <ޗQϞbY2Db}jP<~Z2(%CC?6sPrJtödbKs6-݆T#UZ! ´B!4?d8jVjJ%AX]BqWu# K_HKiM_%PyWԣ#/所j#͉{WnX Ƌ"`]ct8;DIc5 gk9sN TY$,Q s);;]>l;?CrҦ^8Matŋ믿P)֥r:./BH#^ukS>}V(BM{3 DV)U.9h:n<>Uo+@, ȩpJ♰f =_3Ɛ(B?d 9ԮL-BxfY?囿NiCu:gA":z \w4NQmܲtY\!%RO)p ySj|x )]Em1BmB&d PpӏsAQ~.]Bg6嘂 S M˜WfiTYΘ )̠%O8uPX96w1Ah U!ƪ $:V񙍑\ Boq CܾH+L5&f*|?һVHX|&7M b_ ⢲uޏsiKe&w]l߿ט/'ZsvWgج+9aGޚ#v/enA' 5G&}\I,>x %U[ma&bCe^@p95#YrcM O^^Ɔ֨i<_Y[}$ҞaOЖx%@;m!885JqS[k th0x~ ^R&p.|ܢV P8\>_ 8!ԟwev,Xfp^hͲXUa4oPѹo%P0DLQ4gs4כPAvUR0c 4ơWL2.&j-n([״]{[Є7JRd]yȔ FOm,d_踲N-f?ywOBh[=`TہTx;3XQ"c0hqhCݦ -2ۑGƢp+g\N3cƭm^jM9gHM!'ƙGC l^U8 B/"Rq6=3&hA~s PsF@yIah%0q|v+ 8#~j)!CtP$PD)fu7aмج"A%MOoc54*U]&vQr/!&AJX > endobj 221 0 obj <> stream xYKo s {؃쬭K{%u2#ÒSlHvX*n/l^En/teuVZBR?>jLGۋ}ˡꇦںB uӻE bs5(+TPE˗Muw? |f㆛v}QrZ%uڜRQ3iω:-<ݔCWunWg" . g4l9\ȨzնmPrٔ׮^s ToZZpW-|7ZAD訧-SZB1TK461ItA[Cی vMP X+bڂ:BE9uVxs?Bc500`3kb,<%Ȏ. ɢrw򆧧q},=7!ahVwVpzw(,9B}c:]Xnu_BLθ<ۗ.T)ɂZyy@r-嘏 s{h$afzbq)Aon\`FCcGT*jy*sd9ΐm-ipVݰsY!,ꏇ)7e&9C PYvu4 W{ϭ@?y,E*0}nWkpJW2/|P)q j-6q!GFq1h_ "I4OBkJݮI}ek f ʕYԝu~TKk LƄF!D1ӠPD/x 9*P 1t`_(_yRP75)+wIs2wiAcSm 0 uVC6qZ͋lrDϦ:2I~?~t_f Pb #_>$BJMʲ- Vaҿ$piVNQPa,WxUwyo Z'vh6Bu ^zbf'Ȯ{@Oq ,5\FNu= F ht;+Y ]sGqx @bі@OFo5nĝ^PJ;yGa1ױ6C᨝󗒳?M͓NJK.gm÷]u$*זkR'' Pd4Ƕ_+I3xc#U̡ P *Az+x؄d]`NJq8Yӧ0lnc lR2AԄ_īP#@;QD˿N[Ef8'JMڄqD=9B!v"G iTjWb4ZGeHЅ -c'n!\ǜ r*Rh+4'Ej<$F۔p["U-ٜXdh1J?ͦnY Z[`0圬:6A)NΙd`$U3vyjL`|Nxd(!`}sC/SH2BH)b)Nl_C[ Tx9Ip1iϸpwW15W_hq)]hR@@n N4S G G>4hC15 iPv|ƚͣQ/>8F]&W~!`LOqA! Ll}ՄcRV_B0&h&)^,&ߦ%)4i^Y;8?]k]fOyFmlCfӰH@MշfRJEhm!nyk?g W=0гOe(Lḧ k3=~&?D*9f$UD s>g89UTdH! /rDWOX܃:.ZM ȒL%R,+{+2<d:|>;jZB}ϩ 7zr ɓi|YjMJv)E[ĭM t"$MŨś<Գv(ݻ0vnYT=y@\z$o፹4qPz8ҦhQwI`s,卻v,ah-\R n㯷/~A^endstream endobj 222 0 obj 2660 endobj 223 0 obj <> endobj 225 0 obj <> stream xYێ}_#ɰo! 6` ^ 9+9_[5VA҃ԩSEJ؆ kq_1M|m!|c4%Zo7w=- o\HO%9^%Rqj") KLZU_peXZnQ/9bs:- VUMEPaUZ*lp #ZlѾ]0I(uڀrmz4~aSKSuuRHx KDQ𦇱/Rq"-B~9Cմ,a/-UAP*љC؊! !.܃p'a Ir>D;HbfX\jhXpq߹O!R|i;@+t>`д,T1JX7>dmfHvhʼ){C7 )g%Dr占cJ:t1Q?i Ok"gqȕhvu/x`Fawm?V +VJz OBz4kOsYV|OѢ'W ,Ә.f8Gz'$%s .L?AA1-]s!Du@ȥ"8p8G6mT{L>;jw!Ԕ?*i}eZ*昌O>/e=;fNypp~fURSujEY)p).mF#ȅ޶]2QkksSqK$Kpn>w\Pdj킨_E lcY\8^ 7g9K=@7j{RKU!2E0&ev1u(xwPną;au , 7a?px7(m|ȣPgvxݪҷL|9pgSp(i=Db,s!qAj[X/Th~z>z&,M|8vˬ^P328&mQ>b:b!h{Mh&25u9~^њ P=YdCdi!/ c*|ǖ (Bj4^/Y0O\^o?ջu΅fs}ƀCwɼ KUEtrM9z4) G8I(s]pt%G6@)5,p9u)+'G%o6HJiShbz%14Z'Ynŗ*PRܵ%$1Cq1 -qLK LMxz\='+cڐ;^Iʘ(Uscllfkǒn>43կkbY!![tcT~⌉φB@kY$+p2~/7endstream endobj 226 0 obj 2430 endobj 227 0 obj <> endobj 229 0 obj <> stream xYr#3898T2ad; gڲ9P0F-/ 7/~_ٯ~W)V_] l +)zuwBOwHCavn/.a 377!|Hi~}~^[I0vuSߧ '[_6%N n"*w* ua{8ЄY?\m|_,u9|?3Ł}۵#B+YBx"%59* ;9%>dh\t1>\&]Xlpŀ N2cI}<Zd*̖R1(駘V…DMMqz8bi>|)9~^ǜD՜t͘3̈uxo!h}+jc> 0 .;ae%HM2XUN*rKؘ۵l*!T8݃/祧ܹ2ТJ#v- @}.|CJr> %ԇİa&G'>iЗ }?xbʶƀ|E%y4axP9U#'ڰ\#8UY "~XŎ6l3ÐP.0tM%@Rr5_KUf= eu-bȶ)J̓cfw,' Ÿ@^ؽ8XBIHJ^*/3Jg`_H:sއ9%Z؂UIwj'3MHwh]0pj!FH5{BB)[΋QՀW ҴCn]K="pqs}F # Cbw>n#Og 5ȱ5XKY9KfyH(ʔP5%.y֛)9f[s +D "Bh*b k*ӢޖZgUb~xsx*ۊ$ŚGy벭V-*Qמ,䴔R^(cSeX~c[f' lCʲ,np~܌LLS1o8C̯%QC>C3D (@Ue/Bș 1}EfhQ$3Vb27mz@͒i}<$U_}v3(hZ%CM C!PM3(V@''ZTghe ҵp*I"wDR 3?KïQp -[Мtas$DEɳhιK>nX}?(eH xIK\ƞpWF󂐄Qk׹ǂ:. u-23pU"gl[ߍ箱P$n(Œ|1*S(QTh}?ZtagtͶ{<!3aId>S+p(kl>WE^4*ЌC$"O~&DJEtTuS^)E/g']-]f,]_<22t-5"R5m\9\8 ..PbЇ &, ʃO>U'"2LNqv2򋩤XzoIs\@|8);-|Z7$߭ ..@52*)Ql`kxT X  `8y2EہdϙdK&!l6~[h7>8:<ăA=ġbgK">5ir%gg2PBsRBo4(|L)ȬcbvF0lYxv p;\v/1Dendstream endobj 230 0 obj 2406 endobj 231 0 obj <> endobj 233 0 obj <> stream xZKs>ʏ)-ȼ[r%&qt(Yy HJ=3uK O{|yAZk]|w{ RU@Vl)ZnEAY;0ruǝB &}[ݻ e|)A`ڐAu}^L /%sk˒Y?<˥MK&iAA=Kf w~GxGZ;FeAPqQQ0%l>/Cj5 Wժ+Lj4"d MM1%5v&>^x#[Z\S3]IH GZf-Du.rzxIRXq\UwtYOPnlBd}1 4`H/}3%ںlV˗kf$HBQfU;!\i 3Z(4Q[_M<fK"K"BK)*-˦۲o*,3uM'B&pTpݚ!F'Lkr| Ft{/VD.ؙR,} NImW9BUwN=d[7LהL[CABFqpA%}zbg"0^G08iK5DP&iai[}05(SHap A{U fj= 0֚f˽dt>F/_*!K)7] 䢂>y煘mUZܚT}F%e_ [Ic:O2LGg:rt0d~!Cnr qR5YeۖGAiKH) ]Z v7<T$v)sq qrNj}:NN,זN?3@%)h4Gh*6os"IL <f*٪il~sge- OsATY^xw=w<XyJdt71 :z|@H2DCQi+bʢe&. %| ΚƬ3f4hYחuH]wPomvf!aS1\@#9T`Lb&&Qؗ-϶.*yD%FђjB ( 1QK$򤀛0zfM6S ^ ;aMU" 匆Mw$0.T෯~p vCјE(^DSH8)>OjDttAbA, E>>yCO)7!UoŸy gDUJu'iNmR%{!Ct51X= C`8 \[bjJcfʼaC!OARTtf?r߿$r*YR9`k/ @ő(W. dl0] -K c \!6\ssDָ9cSQ @f{@'G2lU}LlS5 ^ì&N]"v.P4IbxMJDEBb=㯣U6;(e}^B E.3~r7,AR l,E=%3}e<фeK1HR0*$즞}Lg;7jˠ:Emn1 T#tbt5ٞEY IsI95_RܧP9Vi-SLO>Tܒ$JUV.# 5$/,\?`U3]ޠxtNpUejWٗ3ks[ v2tCxsl ]1>ypf?+1gXy.M%q !v).&yprG`s@i:9K}#_8w R$Oj!|b4VR VqxBf'`\'8%!G篧t NSC \tmbK͹Cf=? `Ԙ6_48Jcb%)k+F^27ׇt,X_8q|ꧫ$dendstream endobj 234 0 obj 2604 endobj 235 0 obj <> endobj 237 0 obj <> stream xYˎr׻jdatM#vǨI[[d!0" Q,s=X:݇cfߨQJlx _3 iUfrn; LEsw$8qaÝ%\>ql rilAK<K OۗZDO_bNG{zƹ&9*mv}r:RxziuXɼ Ow{>pg7Ds'"ݷ}w-q͹r1SAO7#}iዃO ҂↾i $ց`U^Ycp@B%B߀Q"Kz1ܪt))Ce*n"c4%il;sKM /1܊tv=dF,S!6MBNF4JȨ ݾ!Y@(O v tC& P+ls8 \#! IKEwt6<T:7L棌F5=!$84y`cJ R(GA,c-& %GT?>^> Ur7 -Et)vwuf$2 I_mHTv% $ʤ^VfdЎrR8eA/ aP&aAӵ>U,MlvׇXVTftRp,ƎjX&s. 0\M.(P8ѭ: WG]E2(W?[D>d>@#ڏa,@ QW# 16~~C >Rq&x|:%.n}xyDۂ)G@j67ixml*%bsUEѬ8K$L/U߫| Nrr k8>J= mV!ke;% "^AI ť=LI@*69aivz'rR@ s 8Ζ@TQ>Ph!ޕgYB<]eюZ,Q4M{mjEx1 )Xx'}^!C?ǝW -@M&kTPNI UhrKM[G>%x >1Y 5 SPMf40Р mxcn(J"*KZЯ&QrDM2˦:@ȗv\ +b_&' V ^/zONz!쥧OSq0v6s@XRrۇ}WF"`*#JdADg 5Yh Z/n'%槊vnI KZ@(RX^c2ttHU'= bZKE5W`5x= ;l]} ͓x&>e?F!.Ua=$eU*o;{9$x4ke=p`:"맱q## ;LqT2K%p⁝Rf @(G%wlo`vwFAevud"9ѯ ^ tyxS=Z*,^ N&5EO:(`P]S·C;`İԱ \{>Gη|ͧ (q?bn;HyQwBE؄ؖȇ@Ad7?wMM8- ʵ{8:P~XRB # =RB%$l5Q6 NH(6ɇ5#Xj5)j*b& 'fVFG# >fu*3y :?Cb7u|+2wVW[VS-x:sAmEm%C|ڞv:b7Jo?/٨=vFʥi4U${;Tvk*6b]"\A뇄 N^xoAQz ia{YlŒΔ8P:z 79K *e+=_-h5l`5&YvV7 P4x4ҷ0oc쮜+.Hu}O6_~endstream endobj 238 0 obj 2261 endobj 239 0 obj <> endobj 241 0 obj <> stream xKo7B?nuS4("KF,9R||Cڵ"ww͐ o__O/-oV(}\7\OMOn7Mu};5w77UܲbunD]-"??5N8"m}ֹ$BXݒ!LZ>. ?~~j :3JD Ɗa1wJcH,D6St?~)xjkc=0+AL;.6ncVZ=}Ý 4eE ksD9wKUF>lGJ8?=hkn sFIb&i]cOUL7-C!%FgcѲ KYG5]Z3I*;)YK#R}9^H9Gىi쓀76>vns Qs,;'$UsޟҚЅ9)X Eyg϶llGNIa ϖh6 Cc#G3nͨk5  BNU.J^!WYbT5 8B\e2S 2 d\a"NCǞ y1!ΨCVI!&nC@n,\S`)p-0{ӲBeY<;kz)Vv0k^-JbػZi1;o>&K4Bl2Л_$cV34OIP$`w&RvvZKҙa]T\fxEK @04^=~Ϧ8D!ƧY53X9r'\Qc?aO_ooUT"N_9ΒC.i[Ѕ)OӤʉZa ":!h!2LU>4ffӮ?` ".>:Ne4%wPbe8юjҋ<]r[!vTn3Y^Q6R?V:rkD%XB? ״W8uo+!VK~62le;[hmHq[;\a1nkB] ^Өɉx~h1Y#F ÂЃa5;l#>lɌ.6Cp.4gmOpk Z ?4 ]eqJIL~SHSUr7mfk`dq&] Q JTJPwj%/iŸ I`pc97HN&{p]O̞> endobj 245 0 obj <> stream xXKo7 {*b8|3(rhC Dz8+;ܕDYk')Z؀ oo ~fe,7/JɊͬ?ՄZQT̮G Rwp]mMu}E~6sfoZMZe87-"=oQ 65Uo((at"C@#K*lX9ȇߕ ^UHJbQwtrkU |__Ki~ zυ[fZs&^ƛ Fh؛Ƕj~npbnnI9-?teW,{H%Q^˶͑ Q`4Y3fǦItډ)C me=]Y0'"L?6gDh\ g^n93F Gޛ0 2 3Dq^P>@\z#ڤx~eU}ED /, }pБ})jAVw{n#TwMVILHUi]с]){ TG''J|n!Ri7]ۆwX*M䔂32\zR v/eW*Wr;DYl Pkb> endobj 249 0 obj <> stream xXM6t 6@cKM{Z iݞrV-m$ywH^g @Eμy z/Op4[m $3 LIVGD b V[~ﮮn.n^d2l_R{eu[؝ΗsEw[eFaRM*Bϼ\h$ g5EwhnGv ^c!u]] xhj)BԹ2McJ]3@`̒M046a#4C:2čǕ>KD6e2ňJurXeV=ַ,p{<6~ PmkәpE(3%/` ĔI)5 hMCgXbڥ# E UmAckȺXEZ(\3:W,k{l aFzf%aNA:: Y?oU7 *}Y_7. " 0K?&Gݹ45(]m UiҭB b1wLX&ڽd+,@ $,CvX8G^zDDyT c$ޕb$^̄]IVZ[phݮޑД{1h9$P2rA' f2K/aZDp]{82T*v6Z01n NS|``|oevMHn?"}PAg2cjMTūWHqRևh`恀q .fFل*bi)U}abU+)4gγ][tK788d=9>G %3@ǧW) mLXMz -et=~[ Zax"CFs:w뵃(uϷ}<ߔώTϘE'W< K  c5D{1f>z[-yUAgǛ'M\$ =b &Ianot w>h1ۅ#˝K1XNt)@'-"&Fâ07̖if4`Dk|TpZmKbp& f*T=Z}*>S9SA*$Ӂ}t?I"ߢMJ Djs Uަ3n^eooWendstream endobj 250 0 obj 1560 endobj 251 0 obj <> endobj 253 0 obj <> stream xVKO0"';mĩT KH,N߱gJ(D<> 0gދUvmX_R&7` hZUv(5ȣo!ik~68:5J_tM0s R`me0m<=J6Oai;_Rh궋.`V]6Y,M9p/EW5FUKOWG}]SL|FdU3#XnS @뒭yKd`P}L5>G=v2 τڌP?_)ia@7,Y|*芮|\k 83b}bOwr/w6՗O&QmJbx*;yϚǘG#hTiWEn&<щVד`K(-ud? iϽIz#=_tLC0 fն:U\t[=*4SPr<[^5$ߧH9kyLC55=lPbȕgIˣuҥi&JXD{ Taۓɶlz|xJRt|8dR&:[_El@2.ܚ\J GD3易0{WyVPbú'iJQȪebLtʩRWE>2'sendstream endobj 254 0 obj 746 endobj 255 0 obj <> endobj 257 0 obj <> stream xZr}W|]%;&o8eJ*/X%UOϥ{$)?wvt>}eV/_|5c_) ?`+ K+BKOW% 6okd!4q/KS& k/o5Q(cCB01Fn a*6C|wCuL/sK` )liEU ov{cUm7BB1)d7߇pURZu_jUYI9߼6TxY8F<) ñ[OmߍxYєY9#^I7+ԃޛu|ME>oPf4M+!i_j?a T^I?,a:dfT%#E bgP`%v}8@ƌ`QO9''[JBI:Pl Vk L5n8æS~gD6uGC Ř{I9x`Ot;.5qd[kUFffOe3?2}w滷 pT@ , B)Lu-ʞ*Se07'd%sBe$X/F4h(%Jጀ1NAL(xkkS)QO-ټ#ʓ 51`J?kؘ+>rsGƥT`ѦO1RK,!ŞADCeM'4]H 8`zh~ )&k@q }IR/?OKؿYAqe'2/+5_(TF$ n]S%Pq^t/![( N[K*EѲt:B]07e`C5zȽO ]&W6<+vtb۵$u Io#*6yx.ɒttgVPV<!#鸡Nu؋!%A_mq#]Xб(q}5E7"P}vdNo,Oh255o$q26on? 1JKAdLB͉V Y0b6W"%$0*bہwCrizf,)XkN0EuKDtB*kkIl||Y/?s M!r2%;;׻I&t6 Ñߚ b%Tg9\u$$~$}k~'}OBMMCM]=`? ~zf?F^4Id6?ѩ[<^p6 :1@H:i5Tp]#}h,*Yl9 `g72  C⫋#n˸k6t> ,d6> endobj 261 0 obj <> stream xZKs6k#tL,.yh š4LfHXm<\Fnӿ߯y׷W|5Z\_+ q]Vs}{B_ H6@k|Oq7Y78FBvSEY(S*uq N+-tǥ+s*U.ۮ=k^*8LՅ5,4s* z4jMc]u` M`jF_zj.l\ae.?VCunfB[:O5mSP~^sS/d)iءOc%sfL[1Qq\yO~L ^h>ip40LUM 5#'!SXL0BtP?:]uu ]Uu< Q$m: 5F1ttj>!KY#ƛt?NspM띛]RҚ} gplOͺeȃ Sp:M }\<_Ǎ^Ҧ=r@7o*xB24 aKhMSordK7ShY(χ2\ d-]Ku țEs9X9O}G2RZ)UNXu{7tx`7?۽kƃ#EYh {A Reb:N ;3g WCoIm<4C |vd|K®u.$EI*:' ms!3CKS.R֣d=l9 \КL%ԅ.84u;נ,z:ӭ|ͮX>hR])d]c ɃZ(X| Z)ۡK<[ ǟ B HIlS MX^ffx  0\ .~ȭoH OH:G2i>[`29KD#59!Ḧ ˜]\S[ݝ #}? LW}]!/$,/:%f!Ƭܬ:,HiE> XM1!& %‰"YF+:Ly‘$aoJÝ`C`['fL13TWMFf #g)U *AĈZܬT4$j L_rNjR[ B$+9M*(ҚiLˬpR:n!3dǡSR%=rÐ9F@c/5i@ݷow'EqTA<%HGaO4N "W6fL1kήv\N2+B=2FErlmX=&O@,-./ۺް2ِ9 [Y:@燚{V$U€N>OI-})gtntD۔֫nm ;5縐͠Q赋ȃO|R;cGfx]IQ>{MD]@}O8:DpG-u^JBXZ5K2> hKl[/b}EbgW!fx=*&t葿B w^!QAiR,ֳ3)mW[KƷc3 sl/'a"JR)+? .uh0:QEX#JAl-Y߻X> endobj 265 0 obj <> stream x[I$GġV[Ή}ABlLSݝb{Tu{Bb\"}{/TtC7Wl6^ mntcu6ZJ꓾}x?WV >%]Y*=˜UtPLTZ/~q4_Hy?o+U% "T0I9lR>i*%uZSׇfl&aBUؐJacSJPwVYibuo#ӕ"^Ңi|K->>3{:1tŇhFj *߯V2%/ܵO l{?5)Zz& htk^WD >AKaO%J7`LcHT,;*sjf`rpe ʐT=ĒJҤ!U .y;ω|wvP.L%} _kv'b 64vJU }tB^U<ܖP:v/ F#==WSTU+fΈT"R` }%l ch wCB6" Au`]F)xQ*pBxw\ɒ_KbixD7 }5#w z  ӻ] ma7\Zpڏ>% }Zz+FI JO؍ȩވJiw/ݝn+i<^^ῌhF zcn^߼8z zg}{]}~ߴNLVi\B)i D'WQ00xf>Xb֕NN?nn׾%X&T@<=EU*Œ=OAJ9]dt@}|MC\L) Y0y_>9l“e> WYD%w!Ʉ\PkIJ8׉ 5wV}WPs BiBɦ/00;NI/boe@Qܯn?8+O֜KYWjq!NWkgvvǓ j6f޿d9.VU4#o2mBls,ጌݬcfKJݱ L1ahGS4N+XacU p}>fpOH)T#I9# L"# 9iz_OxT^ wItNǧbTE LKu:!ێe*XQBgi+d>\7^R;3 +tua<ޜcsxfJ~ݷ9]al2HiA& DdjԌc"6l>h8_ *&kA{0Dy mwa@#{`ɦ%mZ\3:* !o1z}T4*u@$nǦO &"?c߾]CPE]N1;sϗ '? Is}q9",8 ;O&drc=AVNLd+6Ֆ<h 8&E990kW;.VM?,>ԝ tU,0g?|Ѝq{VƻՅFr eMB#sC4܁\w T?A~NMRqPjԃ Lq1Qf0Wb vuu8+dpU1=b}*> s5s=-6Pk:dB@b] >ȳC!ʔJ*[V԰YcҨ_%&Z(!|J[CڦِxrNd=DEon h$"tP>hg0rQv(ui7/ ;hD{[0u+s=##8l(I3!C_LM=W&8taZG}DieWɲ4+U+|@,"1$ayq7Y^=r(fȳN<*>}tǺ +bX&V@.'VgSSLM(nkX2'cr$O,{x,e2Dmy9?Uʩ2Seyf77gtBCE[@H(PFv̔mq5`wRVJ` ԰.=Uqt6}| !!Ԓ$cٰgB?@aJI,֐v%WHY(1v]NqtIbswX>ЌǕ1V./8G"֢*te4>]ƚy;Ayω W6A6r.:=|hE Ua܄:e .S>D2#^w5BMj!&3)>u/N){CT.UF,׊B{^ x& $2ǖ)β䀆j uEAhGӃ9:% !'Z(7nHY^d_?pڏ1H8`;W4:_ iɮoDڡ z5 VDyLEL6TrM? omZ$8H,"b PU+ x^M0ؾՇ$#=kWCC W K!==Q#rWtbq7z@`"n#^˒uqZ")da :;^B"v>^Q0#cT.OkXk`0TkGH{8ZE$׮W XiCL"wP۹faq氘.8A^ㅏ_#X^QI4{V\l49ե)dfU_vgk݆bkps;FX hL'YU'XD nr$2l& ;8G$άM< K Y$%n -UM B`ZST5@fl5) P:xZaMͥ^%CRMK;8.7p:>%Ĕ* xr^*\O\G-D)wJ#/:LZ QdlSJCrWF.c^:鴄O ;pTn {\( n/Ӧ!ISTwLV̕8CͶ 5 T)]8.'dT0#8J">zbzjisgVrʲxqX. =ہE~v4ck\Ԧq,Aq(lΗ'yl(I!PG:|(ON@y ] cq ahq>0AsZe`ڌq0kO JqG]b+//WBɊ-07CkѵPt-|z~Cҥ"i9k3+Vq Uꇺ g;?;:rZŕOgK'`GƚY[m2UraQ=ʔ2\Sv tq?Fendstream endobj 266 0 obj 4410 endobj 267 0 obj <> endobj 269 0 obj <> stream xYKܸ/46)fı9'Axzx>WQR/0IUŪM P//>]|0?쎛__ƈUoo/lc5]PRӾ=C6ϟ]B c`7ővtCL&cF9'8شݱ<c3O_- o=}9Z}+)47d\`ڹ,[N0e{燮o㘦t7m׍kߞG 瑥zZ^)GSs2J,MU,\)8ݕ޾oL|ae}6ݰ(:E*chҗGa0Ý?àphT9Bu8[ƪU1Yi泰qCX4Oz;mCI%e _i,Qʧ`X^]TY~]BH~Lei<79F d9ieWJSyb-V۴r q;]5WǮǍu~)!_iANoSBML)˄z7\ dv8S\xrADN- if4p3Q@̌/\LK>Qڦ \$H@T(]@K VO0%@8IT5)O'Ʈ|4wny|dX3[6ZDT6H[F9aί~OQHnP=4sJ(dڍZ <-Dϭ܏`mgM{~;|F$@W%Б ^0G(Uat" X\5/f`F 33$M+ 8wV}` HB|A%>ЙNY AÏ>VMa7I0ˍ(@p BE1 %䲉^(%A6j(ZJEph,,FkCf p}XC"]'0(2O]P@3!`}@V / ak! ~g((a>J3wu_"5x})ZuY.?ԇA)P1oZJK\:2w]ńћб00/Ϳ.6.TͿÓ^w:*@NP(>CSX#~ΠbGb:  ~ڻD/AzDZez/ԐrM3rt!+47EKbj; XYsp+D@AT%'k[X܇S|]LhaBb_b7.L%dU! oqw\0ŷW)5\& yOu,hUn`U' eĚpՙc.&!| OHб_ ^px `M:L'@SWC.V,Κ&@fdxZERxmG$w."~;Vk-lsC9Dӣz(t.5dg!C:T_c!plI Zĭ;Gݾ~,|WpFK:? R~OHsϻq1IP)]az)RtN&ݗ/Z-:/BC1G D sxQ*AhD Ԩ3FIY]uuXg\[v[c`Ĵ9?꾉\cʌG빞 E_e~鈏'4S1O~\3n9Pf aEX7zߏ:endstream endobj 270 0 obj 2541 endobj 271 0 obj <> endobj 273 0 obj <> stream xZKܶo\_ɮڥ'YqtKj;C{fC*> g8tXn,?_xgwodJ˛U/+]Z_/ ]au/ܬ/ڃ{TUptl4k6mC^FW64ݡp)SXϰ׵FZZ+@YK=ڱw?x!EiT"al ~^0%i>*is5lk J_^p2u?YB1al%,d,Zxbă6VxUUT9]P9c6Z!J[V',9}9 MmtLH=(s7pTUt-&xwMsTn*Shnm}rU_JɸC`%w1+if5`uKpix!ҮcO(!}xB 4bDaKEUyzy<*qWS--QSE=|˛ŠHnB^k\g!7zTI{Xl*(!u(ȣ=+D)u@ۉ@t~]rFE?vͺf+En- Z-A)JT0 zieBt?E! @OX-#!%}.@KA/+8bʔ"3G*=zp"#@<%BaΉ->ngJa +@<^+8< **{\̙JDL\ qVkNGDQX`d;U8u$1S|7,Y_7H -B\Jkr}2nZehdZ} Vrʸqfvpb@熃2eYL~P!p@@Y`~.vClZZNr@M'frWO?ϲ Go]7;o 5 [dD]pUuH4Kz5rJIpx|swWHlU.XS- kg zߌ ?YɯX&\@?RXrp(8nayXWPQ<|l-]>Kg2' +~cT{mY^l$b{Xs%9ւthOO uuUO>by:W_'(+EԨ@"jbԙC'>njK*ȶ+1a[vQZ6/1׈ WEڷCPT@JAf%ȅm>kGV-WRWL-tFR&CpXkؽpA{eĂB/6Җ }#F e&U46!,U8+71bXE\$exH/BB s&2bȦA$paqUYNF4fe)J=&Y(i/ckFIl+v:q&]嚠R;.J`rEOYjum)ϷNܐ s% R}66.yE!'F^0CZV8 WvjDyA K ƔB.v0|qgUj$&Y3~zsI3G*%4ڻKD*R% N݉M%j?`f61>KE-qS~Z!G:Zv29"'&*PJǦbы<bdI{hǹBވ'#ȸ ǟ߼]r2 YUo3o>ʂTYU' r~+4S@D1$=AV(eKF P=4%K>ӜNۣzX"0n;acw7;ޕeUZ$:PYbw}W!,E3MͫwPRZ 6X r!L:SCw3?y ^9s2Rߪz(ð}3n6ZȂY ԑg@4 H !மb{l*#zc"|/C~{ԋ<j}I%(ͱ+E~M y5x'ԕ%xQ56 y2vOf>A߼ھY ,L bX]_|"# \^,emB:C^`FƏ`+4؄WXzD37w2,Mr!wlgO͋~^R+.'+BlhW+/[7_ڱ9`Gqk DKz nDccڅuP> "&gts0޳C&//蔑WΆhi`̣R!0]R`-7+KntE6ͧdB<˛A0(YZ]L8R p-^yr }hfy5Ң7WB8M0RKZW{6yP <ؽT空g[q 51XBCvvnus sJnDñFygvckV[!<{d3sB׷JNƅ,I] n s1EXp6K*}ۦ3p:O[@M0$ |Ȇ i0g `q2H*8 x ,fcX 0!-<9  Q|b !β i aQp:]ׇ~x>!fk6 U0k{j+֖@=ayUA~bfXWwUOW aHFe F q ?I[Kt=lim*g;cPD@`㔠Ur2NgΌ/9 ~( ,HZzA*!S:e<]ZJd\ yNctTĭϯUsZk$!S: 3uhWB+ˑRU;aeQPB*]jДTެiĒwtCJsт"Mjak<7- 56 NPbn3kRK:fA D{*k7΅ g`w3w"i nVeWT/F*_4sQUBH 1"o-]H_ٜRwJ@HwX/k"eϹa:NTƘsG GpX9}7l`ur-nd5]6ό%DPѐh޼c#TΥµ(+ Oû@Q0p%Cȿc ʟ D y@ r}m>4Fe갪xsFI#9hc#&rV:;Ti'zz8R$2Ź64 !%0 r sLuNw*JdDPjuT93D?@&:/Mv;TGQι !D,"@ͷ5AKIeQ;  =.CvYM>]&^s-}4],9oRU΂_:qLdK1և>Q`|Mt64Aa#Ϧ^i/(w{ 3ZBЀP`^4&fr$#W5BgJd-gDΟI$ _:k@Zj<#΃, ⺞:39Lڻ^3E]x( ]qv/QHȐ#F._(w6F Z"3k`A&}M@&SXx1T+,Z/G- K}1Zadž+i'itLv tcz7(FS7%teJ:q[vO(]]dfe  "0Y߬/fUfu"?|CL @BAQEJ(.@@?Pb=n@>Z n6OŎ[ZAI fU C (u-+><`S,(lBᠮڵJ+^!jJ LG2ȩDJyXsZB`U $!43yÐj]8a.ՊÜNǻ"rc?bĀ}|B8QJ>#6wَE^֍rWrNćlAa 0rnP;Iz*2bkCx/s\ć7rJi3p >qjvNA<ױS~d{Hn]8Սq+H37~XsJr[OY;UjO@aŀi@t?߭~`COf~tV[A܄?~ͫ׫xn^uo^W׏n?ߦm`-~ *dorp-v9mH}F (o|nSt .cjpY.%D"g2bto`يlc/t%磅B%60#4yԀT}op2h.PK3_RT60GGE V& j %b_>hЗth[_ptcS<;ա!ŞjS+ItS L"IW-l9!_(r!A}>0@ʟaHW1#M^A*=n [pA&+eAp-8X3y(@YIW[|5=n!^-RhKfFBa(p EE;C0bQН̇CK4rVgeʆ|'%endstream endobj 278 0 obj 4317 endobj 285 0 obj <> endobj 287 0 obj <> stream xXM6O /fܽl6>lrѨnji;EUfڎmK$իW?TOܼy\gn38ܟna1~s+ 0_yǛo~ϛdTj&V٬h$`'d^|#y]ph7\c[W]A*&ójn3 k)t՜3*j-ʹ~x1!a/)^#TYJzRJ9s+W[3|)cK TrwƆnʾCᮿFTL%0K޿@Tud6Fo%sd6nKb(=ХK9=aUHƿɴ+J,q'gPM=Y!wr2̨ E㯓!ϩZڮ=9=fx1z.\صh nq^,/:y {ȹ:SC:XmIkșǜC)JN菡oJ'P c;ݥI-7ykiC59_?d*"w+3 HK( jg=_a\CćߏUΡS(g 9^jX&*{`ʴ}ա9T]=qګvJif9?S D.0AkTp svUI᫫/nB ̓@Z6<۔*bh %1cB9+Ts'( #HwݣnaJ3%+M)v0qck( oÜ)[=oq])iI_1ϻMws2Vatinz oqbDyu Xn#+5hTz,EKV񌷵f[Lcϡ ױa[+=B-9#@JLʥ%)vB\eU2_YE5HE˴=MX L>m ~۷6j(8lSGFeωjHRpb 6" !q ȵ"|zX78 b @Kݵ?; }`,펎tx!J5Tb[&".v̵A9kրMݳjصꇶKX WE&:H}IF0"{ApŸ'aw;mHvPn[ g8qt tHR-XAr%\(Ij!1lDW,9`U}2+SBK6DbQ[/ےf\)Fm֨}?1DցF R〙car$ yxl(y73PCӄe;R<=$RG]D8Mg3pͨH'ʴ| MOUP=TTg U>qz"e6c'ֵ@V/QalrU)܁&+1 B2(75LSt9L|Y(mqBEcpHh!7Z_2kؿ`M̮^g{2H>bCqx1=Ɏ&aۼPS2)c&7)I办eFcn5 ;ݿU^ {{8O<Ƞ\ezroaʽ w'IkSl{(5 \:SwrȌsucm~aP79Cp@O~9߾29a2Nݹq9c/RbspdBքgjf8!24ZQ*^09Dn~?/?endstream endobj 288 0 obj 2124 endobj 289 0 obj <> endobj 291 0 obj <> stream xVMoF rlD+'w @M6DGC,")TH~{f(R,|5;;潙YGY1z=DGVDogGD0gl9rp%"FBDb4FD(h5a1ޔUfmN$ApOcjHqNL ! INo/IO0ARcRKoZk#580EXJޏJ4֍!`kkHPo֥C 1n}xiQQ$ʓƸ$%/}cҾ,z?tQS^lAg"2xHM&EE/8T5s$%鍻Zo~PR'0`aʘC-up̂tp2 ЖԴ+G6C)t4n56.Hwu66$#۠gwFeեXrYy q{0]lOa8k %i[j3dJCA0 `pazރjQB]q5NIO Hv,WJKގ.OK %J /eжu흣#{wg&kxR]e-l$HZ7QfNos֓æ6 daB fγ,=_<':-_56[D3YUMZ6 ;Ӱ͢II562o[՟"8Ĉ_Ǐ` Ow: vF֐eB$o?p8a;444_ s}k}xM /%E=imo aX2G[~HU7{w_%].0t:ulg/3qY5΢0@endstream endobj 292 0 obj 1085 endobj 293 0 obj <> endobj 295 0 obj <> stream xVM6OTH c~SMsH ѓ"]=,^%$̛_/3÷`uh1(I]J@p0?À9Nq[̳>ҁD 1ճuV\w  >xB $AEO48SD96&ە( ENFS3 C IK]K5]ʫ;>I lPSP(AIHD2X)ifO).}f؊Jȋ9DMejY L;Ml&߼ypZW{C.u)91rPL9ns,dp!ɴۤHm LSxNWuԡy]M"PHvmj9ʇR͋6oVo:Á%MYy*2uwW!8^G:^bxϻ> endobj 299 0 obj <> stream xVKoHWX - qݓp ɰ&qXiߩ~ړ)HFW qo^Eߢo162We4`4)ID$ "e=\~FFut>]^NkXLf|Fbey[hKNr8YF)GI"ݦDNQJp>)CR(͵ah,{r~A#&;m"o>κbhLm\&aNWY߸BNڮVVykD)bXԉ5@]9+MB/r(ՙlFxJRcͰ6m%g,PzLkg^=1P皜"E8ɳ+Վ| _6ƏQ`v5[Ct;ANP荈 BS~XSybZAqVwYk RiQ &H%܋"LA6ָp++` JOO,Qc5N&=pF)GJ}7Zmb~N4z- zVǷ&%mIVwvZOHh},2RӒ5Cݕ7}/R!9"E#6wɮ'"Vo Z@&<: "U聛 x5ȏ!StΛԜw+7H".uv e<%a6[M1:xmgmya}إ&wG8l {( L \88K8]ѝ9 ;& < _u8]XGUf7:5/D5msf޻uںA&F"pc0w 4M2`=o1沬v0\f!vwf}=8p@~y¿e5TCc՟ XE{ _, _8^nɰA +wyY ԯLQ7 4u ^GTsi|Cv>@ @+e1g<шendstream endobj 300 0 obj 988 endobj 301 0 obj <> endobj 303 0 obj <> stream xWMo8W؋Դ!QJ.ih[$܍DRJBv>ofFx NA8BfO4sqx6J;qXC4Cg E;U5O6}ѡ -r=)6mЮ93hFtLa`@y7>6BPQ&\17&R7* FO v//s33_f9<'>=}S~(f;aയ]_׾"?橭p ҏ>5)+EF95o %?VY.}Kёy L99b;,~...aendstream endobj 304 0 obj 1013 endobj 305 0 obj <> endobj 307 0 obj <> stream xV[o0}﯈ԉ֍oBMJx[Ic;NnB>T.| ?䃫U]Kb0=&AL) gA E>eEOeYJ]Di92˃AL $XwDȕN69 #s%Z8 xnp,m*Dr3QMQ FAJU7'q#!AX\2Ks a؃ fj0s{S[/"٦pUnbnnm ""Κc#r94z4As҆_>s" ¨b`>Fx)ʱVf~"@ݧҗJ)͆y@1Q1Lr6sղH @QGoϾhNx2wWaO)-T_2L% ;CFJeŹ !%><ZÈi yfUq3?K-Kea_2}(5+L0L:{%w0ah"U@?hm@r4̥(~N:m5VX\-s5¨ߑn`-5g]GKsOm;A}@Pg[ڸcolo8}}D R m۹i:gzŭlYaޠHZ4~ڻ̠|h6YU+%*YaD|vP]v]sn"IR5a0na@oJ꾡NjOL~UtquhI:S# MO` {)Pg0[b?* endstream endobj 308 0 obj 828 endobj 309 0 obj <> endobj 311 0 obj <> stream xVMo@WR7. zHr8njۭ¯g8YE"͌߾y31a?-1Eqn@ )E."D "fPTbLڈcgj?0IӬi`CB!"k3 (O&/Wٳ6GP#FƹU`/yr ʻꢉTQR0;[Y^0BBAYf~ZNVKEM71DaSӞ_ai{]}j@;.:= o^bj=rݎ41r쀂\N'EG%6$$:bTBI] ֎նLۼRTaH>߮}11STa:qZǎ\Ӯ./S2O KҞ,A:|pA'&KF z%8ʮߵu\e^Tek$ϥyRhK"Y'VDp8j+% 2tbбQ pOנ3TP/4"GJ/³r_fヴuf020CsERˡhǨ/Ur{1gc7ˋ_{,QYW˲~\ӣy)ģ$ljަO=8<~:>ރiS$+{ꐹjcu~*NzpzcUVOLejv ,( 9桥ЁMpmendstream endobj 312 0 obj 787 endobj 313 0 obj <> endobj 315 0 obj <> stream xVKO0WD9o'۴VEꁶ% $G+Zw8CvU{j?]ރ#?N.H.PB_Iwp&+6?\Q&/ALKF*oPDiDś*Y_\чDŽD%i"jTNN̵qR{ln9b8C06@Ak7wA!ccק?}$Q:ˢnIbH/$",UUVVAc՝RFXZU aEmIiVE*$ٯjhKSXG\:+iub``K- Zҭ|dSd iRGZ~4 *}RNl6[g7yb v>ǺŬ ֱ=}m^u3 i5R*b)b'ܦ>#UûҼ-:Id:$VUSV#O.J<6c+ ۡ'1b{~(cSs炬aRo}UlߥGLvܗ>s;3,!ԐNO'~%@җ@6 , 9b.S/UV$!m;F^̧j`|̚5ZX X3k1¡YOPpuű$##muqbtUaz9UY7<bV>^| (xC-h,`,3'dGՁM?`+o7:Gxues*9oPÆaIEf!hZpmKBwgu .ǥ{_endstream endobj 316 0 obj 868 endobj 317 0 obj <> endobj 319 0 obj <> stream xWKoFW ȀIGɡn`q.YÇ,$^::Py>z_}>z$E{ ǔ!,|)$ͼ/6d]_X/aBYSdș,J8` 1C]ԇkTVMvKvgo~*jY}hVcSf_*QHU7i!+Lժ+BBfYwmہ!&tRY_!_ԓ_ M:^Cqڦ/Vv-_&ЕIZvk Oʠ̪v\-&2N\9nr:wBSNSw:rP=`3CDd8)hBAQ`ҿ[2utI#8:(ת G> endobj 323 0 obj <> stream xmTMo@+PJX#M{hZ5)G.ĬS*΂}XX>`{;ofa8E&yKRk/y}9'iO8J@ D7Iݮ V"%L$ 4וiwׄ.98K DK[6f0_RCw <x fBuc 7/2Z0 TEc<?هXq19ZWiV,kv1?A?=T8?!0Y1ڷH bR%X0"]DøC79nkŠ- ( l(i¯1ءL[٭K^/'ż֭?pCUCd)Kk5YSprR~` 8jw˥hL.ѻ(kwq_0ןS]LjFvB (JBѳB]Ff8vyhLsCa߈ ԭ0"q)yJ+.endstream endobj 324 0 obj 532 endobj 325 0 obj <> endobj 327 0 obj <> stream xWKoF <#8vӴ r(z`%b,Q EI;d1i}(|0<|1%'FG bk*Χr2C8SލV8FSu1]^L껺Yyɟc))ZO,._^)[6$~ҘO G*]v%Rr!oS1a[wHZL|kY”KZnX CDX(&xJ`S*u+dr@Sא .CXrMp0gח؉#n3sN챤0i1R~ L[ _j;]qC*7~lڇe R*R]\eY>>>YhJ5vSw--U~&È9=gXgS$gP&*,%Cɾ8N` ex0`0o|@2 O ̠k61{vYȶڻu/:~NĬ0Ow})rl6Zh@p}ϻk؀~ pK}* Z3,qqʏ>\]6Ԟ/v <gqW`%w6TFhج!Ly , IGs.YYɌ>7̱K Oc{8!{SwS{HϨ;z ߻? mj9%dz8KaYv9zQvԗ;~9x"'=y2B\Ie.xއs:g@m"52|ܷO v9wq&5(fUluwWMH<3n$Xg|qì{G=*wG8b ɸv`!-e_\@єbӃ˫1>Z:,FE84Е!pjnTeT`LP|04-ļMZJU g;15nз=0߁`\ΪJ x|NuYu<|21bؕb< w˺ ^7EdHUuZ)mR%GNVeӦ1evv E}pY|6`<07^.)(LROѱW ap9-ޏޏ,opendstream endobj 328 0 obj 1179 endobj 329 0 obj <> endobj 5 0 obj <> /Contents 6 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 52 0 obj <> /Contents 53 0 R >> endobj 56 0 obj <> /Contents 57 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 64 0 obj <> /Contents 65 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 72 0 obj <> /Contents 73 0 R >> endobj 91 0 obj <> /Contents 92 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 99 0 obj <> /Contents 100 0 R >> endobj 106 0 obj <> /Contents 107 0 R >> endobj 110 0 obj <> /Contents 111 0 R >> endobj 117 0 obj <> /Contents 118 0 R >> endobj 124 0 obj <> /Contents 125 0 R >> endobj 131 0 obj <> /Contents 132 0 R >> endobj 135 0 obj <> /Contents 136 0 R >> endobj 139 0 obj <> /Contents 140 0 R >> endobj 143 0 obj <> /Contents 144 0 R >> endobj 147 0 obj <> /Contents 148 0 R >> endobj 151 0 obj <> /Contents 152 0 R >> endobj 155 0 obj <> /Contents 156 0 R >> endobj 159 0 obj <> /Contents 160 0 R >> endobj 173 0 obj <> /Contents 174 0 R >> endobj 181 0 obj <> /Contents 182 0 R >> endobj 185 0 obj <> /Contents 186 0 R >> endobj 189 0 obj <> /Contents 190 0 R >> endobj 193 0 obj <> /Contents 194 0 R >> endobj 197 0 obj <> /Contents 198 0 R >> endobj 204 0 obj <> /Contents 205 0 R >> endobj 208 0 obj <> /Contents 209 0 R >> endobj 212 0 obj <> /Contents 213 0 R >> endobj 216 0 obj <> /Contents 217 0 R >> endobj 220 0 obj <> /Contents 221 0 R >> endobj 224 0 obj <> /Contents 225 0 R >> endobj 228 0 obj <> /Contents 229 0 R >> endobj 232 0 obj <> /Contents 233 0 R >> endobj 236 0 obj <> /Contents 237 0 R >> endobj 240 0 obj <> /Contents 241 0 R >> endobj 244 0 obj <> /Contents 245 0 R >> endobj 248 0 obj <> /Contents 249 0 R >> endobj 252 0 obj <> /Contents 253 0 R >> endobj 256 0 obj <> /Contents 257 0 R >> endobj 260 0 obj <> /Contents 261 0 R >> endobj 264 0 obj <> /Contents 265 0 R >> endobj 268 0 obj <> /Contents 269 0 R >> endobj 272 0 obj <> /Contents 273 0 R >> endobj 276 0 obj <> /Contents 277 0 R >> endobj 286 0 obj <> /Contents 287 0 R >> endobj 290 0 obj <> /Contents 291 0 R >> endobj 294 0 obj <> /Contents 295 0 R >> endobj 298 0 obj <> /Contents 299 0 R >> endobj 302 0 obj <> /Contents 303 0 R >> endobj 306 0 obj <> /Contents 307 0 R >> endobj 310 0 obj <> /Contents 311 0 R >> endobj 314 0 obj <> /Contents 315 0 R >> endobj 318 0 obj <> /Contents 319 0 R >> endobj 322 0 obj <> /Contents 323 0 R >> endobj 326 0 obj <> /Contents 327 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 5 0 R 19 0 R 38 0 R 45 0 R 52 0 R 56 0 R 60 0 R 64 0 R 68 0 R 72 0 R 91 0 R 95 0 R 99 0 R 106 0 R 110 0 R 117 0 R 124 0 R 131 0 R 135 0 R 139 0 R 143 0 R 147 0 R 151 0 R 155 0 R 159 0 R 173 0 R 181 0 R 185 0 R 189 0 R 193 0 R 197 0 R 204 0 R 208 0 R 212 0 R 216 0 R 220 0 R 224 0 R 228 0 R 232 0 R 236 0 R 240 0 R 244 0 R 248 0 R 252 0 R 256 0 R 260 0 R 264 0 R 268 0 R 272 0 R 276 0 R 286 0 R 290 0 R 294 0 R 298 0 R 302 0 R 306 0 R 310 0 R 314 0 R 318 0 R 322 0 R 326 0 R ] /Count 61 >> endobj 1 0 obj <> endobj 4 0 obj <> endobj 9 0 obj <> endobj 8 0 obj <>stream KXIPNR+CMBX12!Vs^]2FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMBX12Computer Modern)LAncModCpefPrgFshRGtiHuIUwlaxmb:12'3(4)56-"OD.PE$QFG1SH'TI3(UJ)V*6XMBYNC  *B%|pLa-/9  g 1 r D u agf aF{2H Z2Vfn`!`Q 0J^9sry{oruy`Njԋueo0-`싱񋤉,{} ^bCMhi@=`ɒdF`ӋԋFi˰s?F`ӋԋF G h {xpH/!,ƌƉ~|razUFE/'1+!@ vi`uqywyt`ًڋtpsnŒ!`󋭋󋦉!ivv2 o/174#-17Q ݺċpogW[8U B`ɒd^u_F"&3 #6ºWFMaNX~gͺƑy_,WJb!4u@CSHA.%fb))F |,W1J3Shk‹Ժ֋ aDV% +FTurNj&.PXwkcA`ɒdF`ӋԋFP%CXK``lNS> Xq]aSQ06% 0++|{~qJIv[% YWsm% ͛|zvcygD0e%A`F`֋܋4Ћ!>;,9,`!`!/9GI7/}p du3 }zybzW[Elbizxyhg`f{sMtRPHL:Qr[K3r_` 4йF]]F4[~WhhQgñʋ,3 =`!`䋼_BaB_+T#3B  { ?||ofgm*"ura>0vXLo셟zvtxhqwbiUKO䋠Er~ U^b&ZD}mA`ɒdF`ӋԋFi˰s?F`ӋԋFGv!|Ӌ"M`!`󋯋􋨉! qiuHHbih݊噜~x~YlvoK΃! 79PT4;tA[!2zٶs kNIN`!%vocrw~laR(Mdsȋ Ѹ֋ aDnu@CSHA.%fb))ښks? g%$$Sa([((j!tշaWImnlO΢8$sjdblieeiDI`ƒdF`ЋˋM+o@sn gg n`n gg n`!`!!`!^N`ɒdX?^N=@F`ɒd'nՋ:AMg'aa'g```P"dt=oo=t`u#&Q C8n gg n`1]M,d ŋn~gb~g`~"ĶsPpXY`~s?QĶtGpk[m`-yu8mJA`ɒdF`ҋҋF"b4b`B!S 7ayzϴe9c23_0wؼf`}L-7ykZbQދSin`o`4)( tGpmUo`0R'}8`Ǎxva`΋E __d&\G{k{GK+WMv[=`ɒdF`ӋԋFiܿʰu=F`ӋԋFiܿʰu=F`ӋԋFU dJA`ɒdV€%-P\xkb[I^Rsfl[\kGf}8$0$$xjddjlbbljddjlbbl2'kAKux``􋺋2 PkavP|=|/ךϾ *K,V|ll@C=VCb`mwyz88A$4g=jMS^jS_pjdgg2M!#[^!vI[nbanna/U9r/ɋC9P:Y{\ϋFmm]kbqiqewzT x`DDY6qv$GLHLG"u2JE1*Ary|O``*oTBLFG"qyDDYZ6$LH2-o.ċδ]{ZSR[\zx-> endobj 28 0 obj <>stream GJBXPC+CMTT12 ~K'91FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTT12Computer ModernWLAyncXMBzodNCpeZODqfPErgQFshRGtiSHujTIvkUwlaVxmb!]8-"9.#_:/$;0%{<1&|=2}>3(~?4)@5*[6+\7,-"ZOD9.#[PE/$QF;0%RG1&SH2'TI3(UJ4)VK5*WL6XMB7YNC>@\]^ _  ! < = XZ^3V(lZ?b( l : B d NOA[E>vS,$;z5wJrQ"  m !P!"*"#P#$ $Z$$%*Aq}*}pnnj}pnnkjuyJ `wtlkjtnnsl6lsnntj>tNyx w< p}}pnno9R;}R.p}}pnn q4|or\[{tr|ztec싶T6]9G_ezxcI}qnnb}qnn'q}bI϶ŗjR{b}qnn'q}bx\rt}uTZs6C辨b&_?T 7' xp}-}pnn@.:.q},}qnn xx}qnnrw\\zpnnq}w~b{[j4wzV}pnnqrnnrqwlgsouqrnnrqp}Vdq{b{}qnns}pnnZ<ĻPbޣMg\dN7{5iZeM2Suxjjcytyjkjuxi\l00(+[8CMI.+M@C6iI}qnn:{bN!/+)̶a~q}bJS]9@CQ̺PWpOwvJ|rnnu}qnnp}uxqX)8ir|'}pnnxynoyyb]a GK v}{z&z.6++`/ur2^rq}bjpnË7#B]_cI}qnnb}qnn|B1+GGD@f 4T I.')䥑lx|rQJx8Iʀ̽ʋ6Y^uxjjIyt~jkjuxH_vIIvppII^0u~ċˋҖ©݋ڋV\=NM>UXus.}qnnw}qnn["248+ Pr2J kjuxxxfF)-(Դs^}pnn/p}^JSZ8;?M׶:M   q} ыf?|?;`2U(}pnn,}qnnq},R5Cl}pnnp}pnn p}pEVR^H-!zc5hxkjxhcZyti}pnnp}pnnkjuxKLlQ gaYHRmS]i&}qnn?}qnnq}"yFbS̲R]}~ᤅvl|Sy^jajio0EB5uegxomhoTmwUd8L$Ej\Y^ƹ]RR`]V)BЋ3]B_)ґދvIIvppIrMr{ynx}yx<z{*~׋NjҕšꋠtQZ?Viw~t{on̺;hxkjxhnZyti}pnnm}pnn#q}[8&ostc]w7u8iboxy~~Tfih.Kavӆrjxw${ueM [3Oꋏ^qwT6]9G_ezxRiI}qnnb}qnn'q}bI϶ŗjR{b}qnn'q}b]onK̻~«B >|rnnu}qnnp}uoovt`Eniq|͟Яsmscypϋ~sBnOZZ_O/:Lhp}}pnnNp"9:>*.ڻe'r%oz~kjc_ &JM qx3l 6q}6kjty96}pnn.i񟋡jkuvORt=/wrrwwrrw@}qnn}pnnq}(iIovaZRh!??3PdarzouroU`O<5C㊚mjxw{u`Є`cx&tdbj֋c6x~{bh@r|}pnnbhbp}|rnnu}qnnp}u{h{u}pnnq}uT6iI}qnnm9Gu^<iI}qnn/oȋgq}bx<wrrwwrrwO}qnn0{`ofphoswszdyt~nC틳FiZytijjIyt[}qnn5q}["q}}qnn%}qnnq}%h< p}}pnnp}}pnni q}&}pnn6.iN}pnn^}pnn&p}^ 2ksnnq}p~Kp} }pnnJUWVKp} }pnn - xV p}}pnnY{ÁÿjkyKwGnyWp}}pnnhДŽ~o>H6i'}qnn}qnnq}:x2Ra*Fp|roonPQMwYzKgGDOɐjq}qw}Jtvbxhz\P` rNq}-}pnn:~XzEb0p}-}qnngr+q}}qnn@$>q}}qnn +8i}pnntprsnnq}hY3hi{pfu[^lnvym}qnnkunntlL߯h\zpnntlL߯h\zpnntl5iI}qnnytpnË7#B]_ B1+GGD@fߠ,ghZ|dSŋnjƍȎˋwrrwwrrw8)Q!iB}qnn)))}qnnBbۣ8 8=9QWt&keFS)4f%@~kkjy+Y~kkj{ ^{ ^y+.(2.l}~kkja pnkrrkmqpnkrrkmq8t$Wz|vzLv8s\ğr_|p}~2nnp}e~L@Az>kJouye]G2~f}pڋۋgQJ]ʕɀ^TYbYE a pnkrrkmqT \ptq~{~zoiiupnxgdN()KNKcR5Axn^[xp8<荨vx\(1]u}oxhimxonˊ{`:d-Ty!5RɫĠ\(J~y|dyeNnz~x`jVV|vqzËA3}pnnp}3̓2 q}4~onng)pCs_W䂠ƽŋuFWX[,\QMwtvtwswtwjVBB‹dmͥѺrulnkqg_ďR{ŋËĢ?tjPGx8Rwnkli]ʧ8tkjl}C~kYLFj||jjk,l~mkjp->47aŋZ4=POU\pSnkjvs8<Hu}AT:Uyun{~"ij5yXmMk]kthzf.m|unĠ\(Le|yvyLMyv^BUIQM3'^{svw~l4Ko3ȱNj@=[AjЍϽg\nx8"f{w~}n8t@R}i#$ 왕srAQ]JS=o.,.PKN @}|`bspxpijqrxtzy|}lߠ{ϮٸtKTKOh΋hjor|%68Xg}<wrrwwrrw~yhO}qnnDq}Oq}@itsx=n8tUxCiAmtz51n'#(~~zŹݥAw~spz}F䋡}{{}ynwQt}zx|ML|xKzdxy㋧vww{rTb8)Fq}BjkKlBq})); 틟{|xvb2^E 4#XRx`]  uH<4JOC;<P"l}"#kjl}##~kkj##~k8t$-w|zvOz WlJ~kXN q}tjuxgyt lweZz|x~zj_fwnpjsTy  yo endstream endobj 331 0 obj 10148 endobj 26 0 obj <> endobj 25 0 obj <>stream  DZLSSK+CMR10!] N0FKZCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMR10Computer ModernncodNefrgFshtiuTIUwlab.:12'3ODPE/FGSH'TIUJV5*6XMBCXj7;@ 9 v  > MUP+0uLe^HlHLʖUR}^HlWrSዪl}jQB\`oM!lѓZq簤Wbm~qw?>/( n.JfV357ݵʊg0kj' *+ q\`_prȋu``QegUu_u^_nY+0uf|I'E$lѓZln`U ")(˸ayzm[^u`^gsqċǨ|^zy9|2x>lxp\$l$,hZjjZhl\p?qmB{U/$*h$1Yctm͋)ߤd104ߓF$ 㻻y~rtwfVE6V!_ev~|yjhlewOzk`cCT }XVQNr`kv&F5UDY)T#<l؍gg>slً拯j,k6r7j+5 *Sq6t{tdV{CXhc=7gx~yzamjdaSH1l~eb|eHShouq}+0fe]ዪl}jQ7_O{g$lѓZ^HlHLʖUR}^HlWr 'o8A$##Lr9m )uҧrTAmefH EEN l̔Z^HlIrolupqpq+0uex1$E'lѓZF4[GB7'lً2*=0,Ë89A.9xu5+ti}"cl񋹋򋴈c"+j5ipuh> endobj 22 0 obj <>stream ERKWIV+CMBX12~17!Vs$l5FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMBX12Computer Modern5AyncMzodCpeDfPrgFshRtiSuTIvkwlaVxmbW8-{9./012'3456 7,"ZOD.[PE$QF%G1SH'TI3UJ4V5*WLXMB7YNC8om 6mg }?T y" , r h N J 32e2y1?1q Q 0J^9sry{oruy`Njԋueo0-`싱񋤉,{}Q\̶ˋibpR`~ ¶sGonPq`M%}nv^kFEsmlsvi\a݋՟ ^bCMhi@=`ɒdF`ӋԋFi˰s?F`ӋԋF G h {xpH/!,ƌƉ~|razUFE/'1+!@ vi`uqywyt`ًڋtpsnŒ!`󋭋󋦉!ivv 9L*x@꡷w Za!uS02 o/174#-17Q ݺċpogW[8U B`ɒd^u_F"&3 #6ºWFMaNX~gͺƑy_,WJb!4u@CSHA.%fb))F |,W1J3Shk‹Ժ֋ aDV% +FTurNj&.PXwkcA`ɒdF`ӋԋFP%CXK``lNS> Xq]aSQ06% 0++|{~qJIv[% Y^D@B`!`9T(jv,PP*iWbZ |Wh\h]Hj1Wsm% ͛|zvcygD0e%A`F`֋܋4Ћ!>;,9,`!`!/9GI7/}p du3 }zybzW[Elbizxyhg`f{sMtRPHL:Qr[K3r_` 4йF]]F4[~WhhQgñʋ,3 =`!`䋼_BaB_+T#3B  { ?||ofgm*"ura>0vXLo셟zvtxhqwbiUKO䋠Er~ U^b&ZD}mA`ɒdF`ӋԋFi˰s?F`ӋԋFGv!|Ӌ"M`!`󋯋􋨉! qiuHHbih݊噜~x~YlvoK΃! 79PT4;? g%$$Sa([((j!tշaWImnlO΢8$sjdblieeiDI`ƒdF`ЋˋM+L(Ӌ\vP|`ZrY*#?1H\}Wtuv~oX`v<3&wvtPx]~Nj0M^N`ɒdX?^N=@F`ɒd'nՋ:AMgd9'w$+`ۍ򋭋ȋ׋ۉ+$7g'aa'g``QʋgbpR` ¶tEpnQq`DxQ[񗕖ЋqwfqSt`J&`A`ɒdF`ЋЋF 0w}`͋{w},d ŋn~gb~g`~"ĶsPpXY`~s?QĶtGpk[m`-yu8mJA`ɒdF`ҋҋF"b4b`B!S 7ayzϴe9c23_0wؼf`}L-7ykZbQ hՋlCoDt`}ahlk+ek(r` wQދSin`o`4)( tGpmUo`0R'}8`Ǎxva`΋E __d&\G{k{GK+WMv[=`ɒdF`ӋԋFiܿʰu=F`ӋԋFiܿʰu=F`ӋԋFU dJA`ɒdV€%-P\xkb[I^Rsfl[\kGf} 0 ߋYRnBs`}> endobj 14 0 obj <>stream  QRNYHB+CMR12 ip'%/FKZCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMR12Computer ModernQLAyncXMBzodNCpeODqfPErgFshRGtiSHujTIvkUJwlaVKxmbW ]8-{9.:/;01|=2'\3(4)@5"[6+ 7, -"ZOD9.#[PE/$QF0%RG1&SH'TI3(UJ4)VK5*WL6+XMB7,YNC8n>oi  !w< m   RZqO%y\ZBw L (  { ) vTad3k$abx6N4tLhf !9!!"V"#C##$%Fbpc@u'~C4_n3hm2snӎffCwn`DL}}{e}boqKnȍar1mXrnڋӋJy}|``)κn}ktSnty%qZoeRnŋl)_!zmpe^lxxyzvnefôʥPcQ苧tn\i*e8wnϓZ^JnKNəXN^JnYr-^fbszE: .#) uDUmUN:,:ഽ|dE]`XpKtnsz D\s:NA)-'z3yLuz[  p2_j) ,!+ j^f^npƋȪyd]LfhMq\o[]xeP/i?nϓZqv`N'))׷F!GJy}ushbnYqVӗɋ^{zy1v1u-nn\,n,ZZZ}qVooVqn\nI*v/s =Z<^GCK22`c40I8#3E*ESP2*PV/-9KBrdˋ-3\Lnё_j^Jn?0 G4>lnwt|-pC|U 3$* q7Htaa&A1㝧!i`v<gWc,2BF)4a_+1CG(3]%5/*?nӎffCwn;*MU/:"_ދcID&cS-RAjaOb7`V/-KMwf.yd?#-)M^JnKLkynttgfe|?P ڵ5W+/` @& 幹rnt{g[ERf|yndQr]~]gWFR OXY5Ny3𢝋&tybzpxnmv+Bēы4P?\/T=nӎffCwn䋩㋨m4j7u7j-3$V!86y;xullk gIThho€w|uo`r[3I[uyy\jgpImB.lPicQ苧tn\i5cJ}lnϓZ^JnKNəXN^JnYrЗvߨ~:ʋ$nӎffCwn݋ދwCoevFv"MٌXdpYrnow}OYs?noGsnɋc~}77ڍn^qPtnu|uvs9nm9snߖZZ7n݋݋7B Mog{nԋUzk` `deYntzK:=򈏅rXpjNnp -=)~_U_`nshggu{pnljVys/@cca苧tn\i8aO{d}UL*e8wnϓZ^JnKNəXN^JnKNəXN^JnYrP/i;JnϓZfeˋ-T\raiUTHasgwlclYUhz~v"_`_qUsnȊkw,n6sAnnNqny~lg#R4t?onNqneZ|:;|P eKJvil{# T$=n^JnKe^JnK׸{z@`2/f2*fpvԹ7/(=B0iii~Z*^YHB!IC=~cϴWCN_ZQkFVy`yzlN?>W;6B̩FbpQQUp}}}wpv&"ZOsVr|qq{zqMa m3*/1Ջ6Lp{ËċYtZ6|7e+3`Ltrnwtrmx`LtrnwtrmxtrnwtrmxpSB^~~B~`U@Ltrnwtrmxz hDN@ ZnxuqpupvTކgbCZQDnf8oLM*BD?s⋽ů֍`kJxNj׋ы݋՛Ɯξt5REtД~}:FFtcnxuqpuR~}:FFtcnxuqpu`2*2|pv$ݴmebuxe4p M.'2j>oLmÂگ‹fifY`X^xer[gzc`>Ȱ#{z|yxz|}yyx}yP ߮eF} ialn~\>0 Y=n^JnK1~b^JnKpvҗo ul\ɘ֋h!,pd&8Pc)`U@Wvdnxuqpu}KqO_];,@ dJ& ᷽rnt{hYceyrsqSp+W!=n^JnKd^Jn‹ҋwBvpx|z}|~ /@ ߮eeF} ialn~\>BSpokwaY.X =n^JnKe^JnK1~b^JnKr_iz{~v?oa  7 ̛ endstream endobj 334 0 obj 10242 endobj 12 0 obj <> endobj 11 0 obj <>stream NHYTKJ+CMBX10!FA82FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMBX10Computer ModernNTIU/5*61UP5vy\uosor\ċryzynޕeRdaWb\u)"t75v$-\ƋƋ-#;"b)]])b\\틹틴tЀ"ueRdaWb\u (ZC4e)``)e\,cR B^]   7   endstream endobj 335 0 obj 663 endobj 76 0 obj <> endobj 75 0 obj <>stream RBMUEL+CMTI12 g=b1FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTI12Computer Modern/LyncModNpeDfPErgshRGtiSujTIvkUwlaxmb{.:/0\5"6 ,-ZOD.PE/QF%G1&SHTI3(UJ4VK5*WL6XMBYNCoiwm 07EoyRzj  F FGws8?u-oenP ׋cacdcex{hNyx1mrn9` /9no`Խ/Ğ~or}BlcSS^ǮsZ0i'ŋsrqvQk2~Xd1s_XhXk^gvhpowcQXj/YzM_ zZrv[m)|ip1S|ԭt>M\gj^l]tqv`xf~Oƞƭnxu}B}VֺeB ruxsraN!(%O𙒃VC@oXXpƹǼ[l ɋz v{Uʉs yx{zH[mymox"΂hNyx|S/K@!(U#&aiϹ˾\R`skTbDXrh/i3{'}yP}r`-&2LϴNċtw8xhouTpO5QVٗ ɸȑ.}`eɵomnmlnxmTJvyxzH[mymodV//:lexyuqLrh.%]FLWKgitqv`xf~Oƞƭnxv|z‹fVSiwahEbolM錎Vr=z|PfP#%D&C𙒃VC@oXIګ3핝oi :kn?yx{hNyxMQhx(9EH;*Qo5L+X>;S.$n`4ҡ&z?۔xipxdb+rivLzxDO{Na\gr~}zttyjdt܋5n̨- I yx{hNyx|SݲjOy-XS_[0nʨ1~wTbR)nΓ{oyor}w?ys98 bNwkEzWzx‹}hNyxdԙߋ}|x|rvGo]eufW`ptqv`xf~Oƞƭnxu}B}V`%oyt~vzr]!%&/Qkle?:iqtwrzke{̟}RjN>UUؗɸǓ5$4˿Kgm}cK h%`fv{Tps3gYy~dTNh D[pgvk/i3{'}y&~~YzM_ zZrv[m)|ip1S|ԭt>KafoinPvߨPҳ|ދAyx{hNyx|SڜVf{irofw=r۩󔕁}{Wohjkb^9%+yP_Pv~(3Pkhjdbexww)|pp[HfEB#2ڬ5Ћ_|t|J;kXJ^ht;*pgفB&z<1xru|d16yxCw]a(ld)J|}/}zyq{ouLu~Qv)|iq1Rjd/j(ŋxqzyv1a=^OfAy  ubdtw}d^0=$0>D{Wdbkڋ cYExk\ l~zn/Ğ~or}Cwafh]^ǮsZ0i'ŋsrqvQk2~Xd1sdvċtw8xhou`mvtuvt||Y>`e9M{}tutxngxۋҞ oU)X'˳ƽxu|tPⴅFmX{Izv{NL)~f6yxzz{{xx;NjfdfedfyƋ{gG}xxonV/ĝ x wyxrxtpf{q] X}ŭȓsZ0i'ŋsrqvQcgo%{:V՘ik3{'}y&~~c{Hw}DsËt}RxN`y6]ir|vx_P_Vd]SHwy`veʵploPoxm2n'#&RYᢏ%ɋhfgfegx{)kx| I  /ŜG >ms}l7UQƬ皴sZ0i'ŋsrqvQj1\g)uǡOȅ׋ΦӚ'wyxrxtpf{dk |fvUk[XX}ԓ~or}3{'}y|Vh̋|~ZzOiu/oykdxip-&2LϴNċtw8xhouSqO6QVٗ ɸǒ2V֗d+u~ywvTnYlb|plY'Yw׻{[tcluiirU^uy}zqwupcys^NjzvG\TZa؟Nj}/YzM_ zZrv[m)|ip1S|ԭt>MYggT\[QZlgYl]tqv`xf~Oƞƭnxu}B}* X{wuyI~V/ip3{'}y&)gq,T0#]FYbc{{ ^}fVSiwahEbolT{$|gtzolpluzK|rkomoxw7tzolpluzS^Gv LTCP ܋ѻ@`lȶɻ}8.?x|`_+'Awɩz tgvxqCɩz tgvxqv V%Ж||ٙ^waddck~?ՋÒKp^rpZbARqbFiȐǰmVDy?N*_9FX_(@y(LNkp _ftkpu{Cy(LNkp _ftkpu{v$ӵtlZbxVbQ/l05x@WJ )$uH_Zo`WwnӋ>~enrVa9>`ܟ*ԩ&YCwVhċtw8xhou{ Njnlp~~{sDcpx)LzxB L~Zul[gr~}zttyjdt܋5U@Sz9^O]c fkpolov?s`  7 endstream endobj 336 0 obj 7263 endobj 49 0 obj <> endobj 48 0 obj <>stream CVTESN+CMMI12 m iiRFL[fkorwCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMMI12Computer ModernpartialdiffalphabetachisigmanofPrFwaxmb:=<>;@ OPG1S'XBYNC '\Y+*}  5  9MUPH{`/bbmcRbwUs~y^g'zt9ecd{µh?2\Lyrb_kuxziyg}Oȝij{mudlgggx^yq\}ֵ/\M0$!,D$!YfѹåcEcv eNbHTqdP`z-ܞR&y:ғdnzwa_`akwdyhgtHxyy.bvt z4|BYny|xwmze`uKؼw Ћ̨<Ixxȋ|hExy |Os@q~6]]zy[e0!Apۙ-l{uuslYc]yaNccdwmuzg|OǞųznudlgggx^yq\}+yGבzn~}u}zv@wv40fDuq~CynxyɋhExyu<%ǘOɀ׋ͧТŰlsuswpWio-v\oNfcWSy<|nrsda=uzuske{zl<֜ mZ/f%㲯kvz_\be"xS z}xuo_`\btªѺ͘9~pW&((QºPzȋ||PxLbn*|kh,jsܘn,ksxs[aNiOxtMy%UuΠasut[{K|}Q|OnTVrv|xsqvkaxPwp3Qb^hxǭ~Xxp/aibmcw\~W[mxA|q}֥ȍmcSduOs~y^g'zt;fac{ h?HUecPj@2\Lyrb_nt}xeyg}NƞƳ{mudlgggx^yq\}P/i{1gyz|ysynys+cRwyrz~%V )%W@W\`xwkh٬ˏϋ_W[orWrU][]`Lssnwssnw`SA]uA|`o'{}yy`o'xy~w`U@Wxbk{quup}jJQJvCܣҺZh1!$#3N$:%`pul~גEKxAf-{յh{7hQgPUj^v?`  7 endstream endobj 337 0 obj 3466 endobj 42 0 obj <> endobj 41 0 obj <>stream FYFOUY+CMBX12~2a!Vst5FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMBX12Computer Modern=LAyncMzodNCpeODqfPrgFshRGtiSHuTIvUwlaVxmbW8{9.:0123(4)56 7, -"ZOD.[PE/$QF0%RG1SH'TI3(UJ4)V5*W6XMB7YNC8o  m   >BCV_S.M ( < x ' ~  n@6;YqNvr&G9y% aF{2H Z2Vfn`!`Q 0J^9sry{oruy`Njԋueo0-`싱񋤉,{}Q\̶ˋibpR`~ ¶sGonPq`M%}nv^kFEsmlsvi\a݋՟ ^bCMhi@=`ɒdF`ӋԋFi˰s?F`ӋԋF G h {xpH/!,ƌƉ~|razUFE/'1+!@ vi`uqywyt`ًڋtpsnŒ!`󋭋󋦉!ivv 9L*x@꡷w Za!uS02 o/174#-17Q ݺċpogW[8U B`ɒd^u_F"&3 #6ºWFMaNX~gͺƑy_o@6wq`uqywyt`ًڋtpsnt=oo Xq]aSQ06% 0++|{~qJIv[% YL!J!h&rs'gg !pp !gJLe[ԆԋȎߴϷӤƋ܋`^QM'(8YFriT`=^D@B`!`9T(jv,PP*iWbZ |Wh\h]Hj1QV% VfV8hZI#5"#6^F`ӋԋF#a;,9,`!`!/9GI7/}p du3 }zybzW[Elbizxyhg`f{sMtRPHL:Qr[K3r_` 4йF]]F4[~WhhQgñʋ,3 =`!`䋼_BaB_+T#3B  { ?||ofgm*"ura>0vXLo셟zvtxhqwbiUKO䋠Er~ U^b&ZD}mA`ɒdF`ӋԋFi˰s?F`ӋԋFGv!|Ӌ"M`!`󋯋􋨉! qiuHHbih݊噜~x~YlvoK΃! 79PT4;tA[!2zٶs kNIN`!%vocrw~laR(Mdsȋ Ѹ֋ aDnu@CSHA.%fb))ښks? g%$$Sa([((j!tշaWImnlO΢8$sjdblieeiDI`ƒdF`ЋˋM+L(Ӌ\vP|`ZrY*#?1H\}Wtuv~oX`v<3&wvtPx]~Nj0Mo@sn gg n`n gg n`!`!!`!^N`ɒdX?^N=@F`ɒd'nՋ:AMgd9'w$+`ۍ򋭋ȋ׋ۉ+$7g'aa'g``QʋgbpR` ¶tEpnQq`Dx`P"dt=oo=t`u#&Q C8n gg n`1]M,d ŋn~gb~g`~"ĶsPpXY`~s?QĶtGpk[m`-yu8mJA`ɒdF`ҋҋF"b4b`B!S 7ayzϴe9c23_0wؼf`}L-7ykZbQ hՋlCoDt`}ahlk+ek(r` wQދSin`o`4)( tGpmUo`0R'}8`Ǎxva`΋E __d&\G{k{GK+WMv[=`ɒdF`ӋԋFiܿʰu=F`ӋԋFiܿʰu=F`ӋԋFU dJA`ɒdV€%-P\xkb[I^Rsfl[\kGf} 0 ߋYRnBs`}> endobj 34 0 obj <>stream IBSXWJ+CMSS12 Mf"h1FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMSS12Computer Modern5LAyncMBzodNCpefPErgFshGtiSHuvkUwlaVxmb-.>:0&=2()<* , -"ZOD.#[PE/$QFG1&SH'TI(UJ4)VWL6XMB7YNC{ ` m  6+f;/y WPT-Q3n D ]  @   t R sFbpDJ5J!343[J3J3J__ `ΟEP@] >XCVDC<NPh0uY|y}klJN W<~ S[re[DUɢJh-%]fXxS8U޳Χ{nQjP=" ×Q!eJJG6@'9HJsHrMJGW d"]~~y{JJ!4$ ΋%!*VIE5Q!CEE:L)Pc#6p_p( *!( j<CH ֻW DTHNp;JA]W`,=%$×X? s`hr_NCŲwjUP!jJJwcqXJ>Wwq@v<HVPO@XUCSٳL\d{;(3+/;ɇYNV7.ls K.bK~_WA@,'FCAoxyr-C +ukbEn1#$˜PYPm>IЉκƋ}!+/`\A ėjf,GB*1BQn*J E@qtz|fiorbfE4<ُV†ƛd}`n}cZ 'wƅċԱFrRXZVWi+C`mySvPCjȼä`/!7z|}yyxTz|}yyxpLF ɋ>TDp.cՋH5WvQHI|T*e_7DX%dNhp$|T@*_7D`X%Og=2.0"$HM7VVtX:::JpƠ'(uDyȝ}x'D!y|y{u!(x}zzNNyzz!t $>YSK::>AP ǫwlARD*/BQ`D::r;,@\YA ȕlh0CF&1BQ ȕlh0CF&vJXa  7 ו endstream endobj 339 0 obj 4424 endobj 32 0 obj <> endobj 31 0 obj <>stream JTQBNU+CMSS10 N{gD1FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMSS10Computer ModernnBodNCerFsGtia-+7O#PE/$FS'T(UJB @@{"L}O5MUP ZG|1ZGCVͤDkJJ$4!ŋ ".VIODG%N;@3Qip' *( p;FC ׼W GPAoFJ@VSf(=#%ƔW=}ykntwrqMBƸtkUP!pJJb6zJ=xa~u=LUITAXUDW޲CX^F+2,-7"ڻrpq=2odbNg1!" ģLWIt;K҈ċi/U21j2TZl]EV9qyDDGUPMAWbXr!Zkxu€VF<|:SyG{o܋‹fdpYT]ӋiuXfNDSz[r'::|XUY-P>[`#9IڭipC!!F4QOEы{qvkkf}》''7<22'@P0$/a#B/J^{\pIcA`*a3`:Ÿg=xrixnye[^6MUPUUQ 8ͳ-zx}wwx}}xww}x| (72\~#j3DuJWb  7 Ȏ ٖ endstream endobj 340 0 obj 1500 endobj 103 0 obj <> endobj 102 0 obj <>stream GIEKRI+CMEX10!sp}vFL[hvCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMEX10Computer ModernparenleftbiggparenrightbiggsummationdisplayradicalbigradicalBigXpqMUPpɠ 6PfHH5Bً9"ݷ}}||"^9 tbgpɠ 6-x1 web}gH5ًg9<"88aa^9q rd#!Gmv)qT钓f}zY{^: btxh|x<xv~i {| ߟdk7|zȐ|~~wy  yo endstream endobj 341 0 obj 804 endobj 88 0 obj <> endobj 87 0 obj <>stream ZNHEGC+CMR10~58!]*X3FKZCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMR10Computer Modern#yncModpeOfrgFshRtiHuTvwlaxmb].;4[, ZOD.PEQF0GSH'TI3UJ)V5WXMBYNC><  $+%V1> ' $   c(OMUPau3ʾtnty\ulzr&qIzpcmlh*~}j~uToFKtxxtwu~m^gʋģ+0uLe^HlHLʖUR}^HlWrSዪl}jQB\`oM!lѓZq簤Wbm~qw?>/( n.JfV357ݵʊg)t=l؍gp\$l$~ g>slϋ΋s>=qyo0kj' *+ q\`_prȋu``QegUu_u^_nY+0uf|I'E$lѓZln`U ")(˸ayzm[^u`^gsqċǨ|^zy+0V,rC3 E7Acep}T{U׋ *=a_uw$lҒ_h^HlH<qmB{U/$*h$1Yctm͋)ߤd u8_e`+6JG-4ba.0FJ+3^+&#D7/104ߓF$ 㻻y~rtwfVE6V!_ev~|yjhlewOzk`cCT }XVQNr`kv&F5UDY)T#<l؍gg>slً拯j,k6r7j+5 *Sq6t{tdV{CXhc=7gx~yzamjdaSH1l~eb|eHShouq}+0fe]ዪl}jQ7_O{g$lѓZ^HlHLʖUR}^HlWrpu?zt%Fs:(~ȋ'l؍gg>slҋҋs>iglHJceg…ٜݧ~DhyrZ}~yZT 'o8A$##Lr9m )uҧrTAmefH EEN l̔Z^HlIrolupqpqɪhAee@hl؍g~~hAee@hl؍gg>slՋ֋s>g>slՋ֋s>+0uex1$E'lѓZF4[GB7'lً2*=0,Ë89A.9xu5+ti}"cl񋹋򋴈c"+j5}4͌tnts]zltx%mStpcmlʋl$}8Aunun^xlyn~0(nXsmnml~mx5,mStVglk}EJ$lѓZ^HlHzk廡[7Ԥ{cașrSQrj\gdY[9F\Imz]d2fXELm@irW_:r͋8',Ci^Zbµ9qmqmV~l~{{~M==qI{v\plыO\O\R[QPvluӨdY{|plÍUytAUPuLee^HlHLʖUR}^HlHLʖUR}^HlWrSዪl}jQ7_Oze}A^B\`oM!lѓZ+0$f@ $lѓZ|tY )=`\y{b“qebRbfOn^vlejU^gs},3c,*cTsnnssnnsU@oTsnnssnnsxo=KAUhypqqpaD94gAvlvA'[{i ,*,|U@G}KoN_\schypqqpGUPfT$ 仺y~rtwhSP_enSp,W> endobj 84 0 obj <>stream  WDSZIK+CMR7p81,FJYCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMR7Computer Modern123UZ9q%oKL0bg͋§j~'ege'9qJiuP~>}L讧Ǻɋ !74/XWϡ=:4MFkgo09qw!¨oU CfQc3asloozb-N K֋06U;ixwlЖu4a}]qljeoYr}w?na  7 ڛ endstream endobj 343 0 obj 560 endobj 82 0 obj <> endobj 81 0 obj <>stream TTCVLS+CMSY10nTfYQFL[fmyCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMSY10Computer Modernapproxequalradicalasteriskmathpt'OUàO3"62" 4 ͳ-x}wwy}}xww}xwwx} z)zx}}xww ò-eXzGEaKO^G`VK/AE.uީNjm^ohˋ~z|RM[DO^G`VK/AE.uީNjm^ohˋAUPTSF(@ZG}\D0yqpq~}%"ϔ}~.4r{~~~3~~}{{!H!G{{}8|u?  7 endstream endobj 344 0 obj 853 endobj 79 0 obj <> endobj 78 0 obj <>stream  QJNQOA+CMR8 glZ=-FJYCopyright (C) 1997 American Mathematical Society. All Rights ReservedCMR8Computer Modernnehtixm01=2345OFIUJYNbwBb*NHmze"(^Cs%jϓZ^KjKIۼȚ`Ly^KjK8c81xZ!*$'~g*5y^]mmˋ ࣰ !tNHemze"3]Syk)jϓZ^KjKIۼȚ`Ly^KjK30 8N"))Ll0b,.o yγlUFlfeH'$ Wtjnqtkhv*Nj˔Z^KjËNjJ0~ʌd^Plj~~}LA<cSyXmjϋNabamgmglhrofb?jȋ~jkjt}sjMqum@nnh/]L{nyRJ*]F~p%jϓZ^KjKIڼ̖XTy^KjKIڼ̖XTy^KjK@v }j_:T]-}ogB(WI&;AxᚽӎtiFvċ鋿Ћі ٕz0T@GP1Sv"F}j@pbcVs,jŦf,hjዸ⋴h,:f` :Wos| |stt hw||wtt@t`‹ ' <02ZYΤ<7#B? rGlwYx?{F@vťbڽQ"DfVT@htkpozc,MI%ڧϋ1# :Q?f{ku؎׃$Ena}{mltNx~}@^v~}cj8f>tjċċt>!@@vF 㚖d|]UPZl~yyË]xg]dfUo_xmclVJKt̎nwyk`107 Y\{iev?oa  7 כ endstream endobj 345 0 obj 1938 endobj 170 0 obj <> endobj 169 0 obj <>stream JKTGVZ+CMTT12~aa ~K(R?4FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTT12Computer Modern AMNCDFRGI"./$%'3(* |f R `wtlkjtnnsl6lsnntj>tNy~b{[j4wzV}pnnqrnnrqwlgsouqrnnrqp}VdqpOwvJ|rnnu}qnnp}uxqX)8ir|'}pnnxynoyyb]a GK v}{z&z.6++`/u}qnnw}qnn["248+ Pon̺;hxkjxhnZyti}pnnm}pnn#q}[]onK̻~«B >|rnnu}qnnp}uoovt`Eniq|͟Яsmscypϋ~sBnOZZ_O/:Lhp}}pnnNp"9:>*.ڻe'r%oz~kjc_ &JM qx"q}}qnn%}qnnq}%Ty  yo endstream endobj 346 0 obj 1353 endobj 167 0 obj <> endobj 166 0 obj <>stream KCSKLK+CMTT10o<50FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTT10Computer ModernNCF/$'uS}vR|pG|rml}r|rmls{roLhs{!{slmoxmy~rj]XJKstn):sb05*)]:z{jjи?b{ih{bjVxui{rlm}j{rlm.s{WSy  yo endstream endobj 347 0 obj 552 endobj 128 0 obj <> endobj 127 0 obj <>stream  NJELXV+CMSY8 mO>95FKZ_Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMSY8Computer Modernprime0*i:f`wPwu}}utt 1`qromcjF}~s? endstream endobj 348 0 obj 302 endobj 121 0 obj <> endobj 120 0 obj <>stream QMQUZG+CMTI10"7z]o]B3FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTI10Computer Modern ncoderstilaxmODPEFSTUJMBYN#{7"5e027Ѐ-ÝzC׋^vM_ }c~iv_g)~|pp)QvTZ9[O~xi`gwt{mxc~Pўn{x{@|`ڸaG Yb{{`K"&(IA-2zUr̽bp-P4"&$M"'dfӽȻ_P`u$kOnTX^Zp-f1(x~Q~|pa ,&6E֯Zvŋu}SxLiqPlN;T\Ʃ ȹo{nx`p;{Ύګ̋fK"#&E16A-2zJҲ8򎕋pi!9jpښዐ}hk~xJjFgY{udjgwt{mxc~Pўn{x{@|p.ϽkpzbI b+W>}Ukp;$uQ{~vtoz^Ng*=bogdL7C${;(zrv|d36zxC{}Yc)yy[c@Rp2-úa#~|np+Q}ĠĠĕef-i)ŋtsnxlnt~jjj}ewdb*tw}vpwor01(x~w|VfˋrzExaoqp-w zkixkk ,&6E֯Zvŋu}SxLiqPkM:T\Ʃ ȹWapqۙn*cf~xQnTjZxuZr'UtῲtcsZfb]{Jv;Oszv~rrvjdtR{p5Ydawpɓº}1Ѐ-ÝzCЋX|pnJ|4ЋX|M_ }c~iv_g)~|pp)QvQ\EY_m_W^OZji]i`gwt{mxc~Pўn{x{@|u?sa  7 endstream endobj 349 0 obj 2193 endobj 114 0 obj <> endobj 113 0 obj <>stream  TPCECS+CMMI8sF=61FKZ\Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMMI8Computer ModernpiniOJhMUPА\8΋v\TaxZ~j~Ro s9ade{WC:WZne_Xgrt{ls\YƵruyxj|P}{Iq}Q|rtiЬ{tqnsmu{&pIpE|zZg t;bbccX*a ~깦o}mb" v0yAA`gav|݋ rzveZ[ggo]Ƌn!V fB|wvv?`  7 endstream endobj 350 0 obj 723 endobj 283 0 obj <> endobj 282 0 obj <>stream GCKTKU+CMSS10~11bN{I<4FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMSS10Computer ModernodeulbPEFVMCO:SMUPip' *( p;FC ׼W GPAoFJ@VSf(=#%ƔW=}ykntwrqMBƸtkq=2odbNg1!" ģLWIt;K҈ċi/ZGP=@ShKD=C[қ[》!0J@Jo1J@Jٹuh΋G-dMZON‹`7`NdjruJWb  7 Ȏ ٖ endstream endobj 351 0 obj 658 endobj 280 0 obj <> endobj 279 0 obj <>stream HUIXAF+CMTT10~118oO>4FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMTT10Computer Modernab/=23*+BC 7xw WT1`Mvlr|alU$`%Bm{׿lDEY+FJɒN7tvdygz^Mb%T|:JiB{rlmwva2![^yliYB4+EJE=h8t,^|wu|~F{yzWTMf}}fhh,jjhh}lihxoƾֶùˋ & <96f‹]3EZQIT^OpcïċB E[Fj}ҋϽg^m|hpukqrgR|nlHQNSL0c>qxrtu|k2Q\I8CcC͋jjjnIq}w~y|xPP~wnӉqInj͉ifa{rGqܠ=>Q"h"#hhh#"hhh"#hSy  yo endstream endobj 352 0 obj 1097 endobj 201 0 obj <> endobj 200 0 obj <>stream SDKIIU+CMSS12~c9 Mf T4FL[Copyright (C) 1997 American Mathematical Society. All Rights ReservedCMSS12Computer ModernyncMzodCpePErgFshtiSHuvUlaVxmbZOD.[PE$QF1&SH'TIUJ4)VW6MB7YNCW7q\WxV6c,fFbp`ΟEP@] >XCVDC<NPh0uY|y}klJN W<~ S[re[DUɢJh-%]fXxS8U޳Χ{nQjP=" ×Q!eJJG6@'9HJsHrMJGW d"]~~y{)Pc#6p_p( *!( j<CH ֻW DTHNp;JA]W`,=%$×X? s`hr_NCŲwjq@v<HVPO@XUCSٳL\d{;(3+/;ɇYNV7.ls K.bK~_WA@,'FCAoxyr-C +ukbEn1#$˜PYPm>IЉκƋ}!n*J E@> endobj 115 0 obj <> endobj 10 0 obj <> endobj 104 0 obj <> endobj 89 0 obj <> endobj 178 0 obj <> endobj 164 0 obj <> endobj 86 0 obj <> endobj 165 0 obj <> endobj 176 0 obj <> endobj 162 0 obj <> endobj 83 0 obj <> endobj 163 0 obj <> endobj 80 0 obj <> endobj 77 0 obj <> endobj 50 0 obj <> endobj 179 0 obj <> endobj 43 0 obj <> endobj 284 0 obj <> endobj 36 0 obj <> endobj 281 0 obj <> endobj 33 0 obj <> endobj 202 0 obj <> endobj 30 0 obj <> endobj 171 0 obj <> endobj 27 0 obj <> endobj 168 0 obj <> endobj 24 0 obj <> endobj 129 0 obj <> endobj 177 0 obj <> endobj 16 0 obj <> endobj 122 0 obj <> endobj 2 0 obj <>endobj xref 0 354 0000000000 65535 f 0000153095 00000 n 0000252858 00000 n 0000152567 00000 n 0000153143 00000 n 0000143557 00000 n 0000000015 00000 n 0000000383 00000 n 0000153536 00000 n 0000153198 00000 n 0000241814 00000 n 0000191249 00000 n 0000191031 00000 n 0000241147 00000 n 0000180685 00000 n 0000180158 00000 n 0000252033 00000 n 0000000402 00000 n 0000000432 00000 n 0000143717 00000 n 0000000486 00000 n 0000003227 00000 n 0000173833 00000 n 0000173449 00000 n 0000251117 00000 n 0000169782 00000 n 0000169489 00000 n 0000250174 00000 n 0000159237 00000 n 0000158638 00000 n 0000249146 00000 n 0000216707 00000 n 0000216451 00000 n 0000248122 00000 n 0000211925 00000 n 0000211527 00000 n 0000247170 00000 n 0000003248 00000 n 0000143861 00000 n 0000003357 00000 n 0000005158 00000 n 0000204011 00000 n 0000203605 00000 n 0000246136 00000 n 0000005179 00000 n 0000144005 00000 n 0000005277 00000 n 0000007195 00000 n 0000200037 00000 n 0000199738 00000 n 0000245480 00000 n 0000007216 00000 n 0000144149 00000 n 0000007314 00000 n 0000008727 00000 n 0000008748 00000 n 0000144293 00000 n 0000008802 00000 n 0000010430 00000 n 0000010451 00000 n 0000144437 00000 n 0000010505 00000 n 0000012121 00000 n 0000012142 00000 n 0000144581 00000 n 0000012229 00000 n 0000013432 00000 n 0000013453 00000 n 0000144725 00000 n 0000013518 00000 n 0000013977 00000 n 0000013997 00000 n 0000144869 00000 n 0000014062 00000 n 0000018115 00000 n 0000192373 00000 n 0000192012 00000 n 0000244897 00000 n 0000226952 00000 n 0000226695 00000 n 0000244472 00000 n 0000225742 00000 n 0000225493 00000 n 0000244013 00000 n 0000224833 00000 n 0000224612 00000 n 0000243642 00000 n 0000219822 00000 n 0000219496 00000 n 0000242927 00000 n 0000018136 00000 n 0000145021 00000 n 0000018311 00000 n 0000020539 00000 n 0000020560 00000 n 0000145165 00000 n 0000020658 00000 n 0000023189 00000 n 0000023210 00000 n 0000145309 00000 n 0000023286 00000 n 0000027315 00000 n 0000218591 00000 n 0000218309 00000 n 0000242402 00000 n 0000027337 00000 n 0000145463 00000 n 0000027449 00000 n 0000031297 00000 n 0000031319 00000 n 0000145618 00000 n 0000031453 00000 n 0000035667 00000 n 0000234906 00000 n 0000234689 00000 n 0000241333 00000 n 0000035689 00000 n 0000145773 00000 n 0000035847 00000 n 0000040358 00000 n 0000232393 00000 n 0000232155 00000 n 0000252624 00000 n 0000040380 00000 n 0000145928 00000 n 0000040527 00000 n 0000045065 00000 n 0000231752 00000 n 0000231552 00000 n 0000251709 00000 n 0000045087 00000 n 0000146075 00000 n 0000045210 00000 n 0000046027 00000 n 0000046048 00000 n 0000146222 00000 n 0000046114 00000 n 0000048501 00000 n 0000048523 00000 n 0000146369 00000 n 0000048633 00000 n 0000049719 00000 n 0000049741 00000 n 0000146516 00000 n 0000049785 00000 n 0000051425 00000 n 0000051447 00000 n 0000146671 00000 n 0000051513 00000 n 0000053183 00000 n 0000053205 00000 n 0000146818 00000 n 0000053315 00000 n 0000057047 00000 n 0000057069 00000 n 0000146965 00000 n 0000057201 00000 n 0000058068 00000 n 0000058089 00000 n 0000147112 00000 n 0000058166 00000 n 0000061088 00000 n 0000243940 00000 n 0000244385 00000 n 0000243575 00000 n 0000243786 00000 n 0000230899 00000 n 0000230682 00000 n 0000250752 00000 n 0000229226 00000 n 0000228992 00000 n 0000249657 00000 n 0000061110 00000 n 0000147259 00000 n 0000061294 00000 n 0000064014 00000 n 0000243867 00000 n 0000251946 00000 n 0000243508 00000 n 0000246055 00000 n 0000064036 00000 n 0000147406 00000 n 0000064207 00000 n 0000066244 00000 n 0000066266 00000 n 0000147553 00000 n 0000066376 00000 n 0000069607 00000 n 0000069629 00000 n 0000147700 00000 n 0000069750 00000 n 0000072155 00000 n 0000072177 00000 n 0000147847 00000 n 0000072298 00000 n 0000073606 00000 n 0000073628 00000 n 0000147994 00000 n 0000073705 00000 n 0000076033 00000 n 0000238448 00000 n 0000238172 00000 n 0000248557 00000 n 0000076055 00000 n 0000148141 00000 n 0000076178 00000 n 0000078758 00000 n 0000078780 00000 n 0000148288 00000 n 0000078892 00000 n 0000081663 00000 n 0000081685 00000 n 0000148435 00000 n 0000081821 00000 n 0000084345 00000 n 0000084367 00000 n 0000148582 00000 n 0000084455 00000 n 0000087232 00000 n 0000087254 00000 n 0000148729 00000 n 0000087355 00000 n 0000090089 00000 n 0000090111 00000 n 0000148876 00000 n 0000090212 00000 n 0000092716 00000 n 0000092738 00000 n 0000149023 00000 n 0000092839 00000 n 0000095319 00000 n 0000095341 00000 n 0000149170 00000 n 0000095442 00000 n 0000098120 00000 n 0000098142 00000 n 0000149317 00000 n 0000098243 00000 n 0000100578 00000 n 0000100600 00000 n 0000149464 00000 n 0000100699 00000 n 0000102667 00000 n 0000102689 00000 n 0000149611 00000 n 0000102755 00000 n 0000104258 00000 n 0000104280 00000 n 0000149758 00000 n 0000104381 00000 n 0000106015 00000 n 0000106037 00000 n 0000149905 00000 n 0000106125 00000 n 0000106945 00000 n 0000106966 00000 n 0000150052 00000 n 0000107032 00000 n 0000110813 00000 n 0000110835 00000 n 0000150199 00000 n 0000110945 00000 n 0000114170 00000 n 0000114192 00000 n 0000150346 00000 n 0000114280 00000 n 0000118764 00000 n 0000118786 00000 n 0000150501 00000 n 0000118920 00000 n 0000121535 00000 n 0000121557 00000 n 0000150648 00000 n 0000121667 00000 n 0000125771 00000 n 0000125793 00000 n 0000150795 00000 n 0000125894 00000 n 0000130285 00000 n 0000236972 00000 n 0000236717 00000 n 0000247753 00000 n 0000235958 00000 n 0000235730 00000 n 0000246728 00000 n 0000130307 00000 n 0000150950 00000 n 0000130476 00000 n 0000132674 00000 n 0000132696 00000 n 0000151097 00000 n 0000132784 00000 n 0000133943 00000 n 0000133965 00000 n 0000151244 00000 n 0000134066 00000 n 0000135101 00000 n 0000135122 00000 n 0000151391 00000 n 0000135210 00000 n 0000136272 00000 n 0000136293 00000 n 0000151538 00000 n 0000136370 00000 n 0000137457 00000 n 0000137479 00000 n 0000151685 00000 n 0000137523 00000 n 0000138425 00000 n 0000138446 00000 n 0000151832 00000 n 0000138490 00000 n 0000139351 00000 n 0000139372 00000 n 0000151979 00000 n 0000139416 00000 n 0000140358 00000 n 0000140379 00000 n 0000152126 00000 n 0000140423 00000 n 0000141491 00000 n 0000141512 00000 n 0000152273 00000 n 0000141556 00000 n 0000142162 00000 n 0000142183 00000 n 0000152420 00000 n 0000142227 00000 n 0000143480 00000 n 0000143502 00000 n 0000158616 00000 n 0000169466 00000 n 0000173427 00000 n 0000180136 00000 n 0000191008 00000 n 0000191991 00000 n 0000199716 00000 n 0000203583 00000 n 0000211505 00000 n 0000216429 00000 n 0000218287 00000 n 0000219475 00000 n 0000224590 00000 n 0000225472 00000 n 0000226674 00000 n 0000228970 00000 n 0000230660 00000 n 0000231531 00000 n 0000232134 00000 n 0000234667 00000 n 0000235709 00000 n 0000236696 00000 n 0000238150 00000 n 0000241125 00000 n trailer << /Size 354 /Root 1 0 R /Info 2 0 R >> startxref 252910 %%EOF iminuit-2.24.0/doc/notebooks/automatic_differentiation.ipynb0000644000000000000000000035440114332717401021247 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Automatic differentiation with JAX\n", "\n", "Here we look into automatic differentiation, which can speed up fits with very many parameters.\n", "\n", "iminuit's minimization algorithm MIGRAD uses a mix of gradient descent and Newton's method to find the minimum. Both require a first derivative, which MIGRAD usually computes numerically from finite differences. This requires many function evaluations and the gradient may not be accurate. As an alternative, iminuit also allows the user to compute the gradient and pass it to MIGRAD.\n", "\n", "Although computing derivatives is often straight-forward, it is usually too much hassle to do manually. Automatic differentiation (AD) is an interesting alternative, it allows one to compute exact derivatives efficiently for pure Python/numpy functions. We demonstrate automatic differentiation with the JAX module, which can not only compute derivatives, but also accelerates the computation of Python code (including the gradient code) with a just-in-time compiler.\n", "\n", "[Recommended read: Gentle introduction to AD](https://www.kaggle.com/borisettinger/gentle-introduction-to-automatic-differentiation)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Fit of a gaussian model to a histogram\n", "\n", "We fit a gaussian to a histogram using a maximum-likelihood approach based on Poisson statistics. This example is used to investigate how automatic differentiation can accelerate a typical fit in a counting experiment.\n", "\n", "To compare fits with and without passing an analytic gradient fairly, we use `Minuit.strategy = 0`, which prevents Minuit from automatically computing the Hesse matrix after the fit." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:37.436843Z", "start_time": "2020-02-21T10:26:37.432080Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "JAX version 0.4.8\n", "numba version 0.57.0\n" ] } ], "source": [ "# !pip install jax jaxlib matplotlib numpy iminuit numba-stats\n", "\n", "import jax\n", "from jax import numpy as jnp # replacement for normal numpy\n", "from jax.scipy.special import erf # replacement for scipy.special.erf\n", "from iminuit import Minuit\n", "import numba as nb\n", "import numpy as np # original numpy still needed, since jax does not cover full API\n", "\n", "jax.config.update(\"jax_enable_x64\", True) # enable float64 precision, default is float32\n", "\n", "print(f\"JAX version {jax.__version__}\")\n", "print(f\"numba version {nb.__version__}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We generate some toy data and write the negative log-likelihood (nll) for a fit to binned data, assuming Poisson-distributed counts.\n", "\n", "**Note:** We write all statistical functions in pure Python code, to demonstrate Jax's ability to automatically differentiate and JIT compile this code. In practice, one should import JIT-able statistical distributions from jax.scipy.stats. The library versions can be expected to have fewer bugs and to be faster and more accurate than hand-written code." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:37.594856Z", "start_time": "2020-02-21T10:26:37.585943Z" } }, "outputs": [], "source": [ "# generate some toy data\n", "rng = np.random.default_rng(seed=1)\n", "n, xe = np.histogram(rng.normal(size=10000), bins=1000)\n", "\n", "\n", "def cdf(x, mu, sigma):\n", " # cdf of a normal distribution, needed to compute the expected counts per bin\n", " # better alternative for real code: from jax.scipy.stats.norm import cdf\n", " z = (x - mu) / sigma\n", " return 0.5 * (1 + erf(z / np.sqrt(2)))\n", "\n", "\n", "def nll(par): # negative log-likelihood with constants stripped\n", " amp = par[0]\n", " mu, sigma = par[1:]\n", " p = cdf(xe, mu, sigma)\n", " mu = amp * jnp.diff(p)\n", " result = jnp.sum(mu - n + n * jnp.log(n / (mu + 1e-100) + 1e-100))\n", " return result" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's check results from all combinations of using JIT and gradient and then compare the execution times." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:37.890967Z", "start_time": "2020-02-21T10:26:37.886224Z" } }, "outputs": [], "source": [ "start_values = (1.5 * np.sum(n), 1.0, 2.0)\n", "limits = ((0, None), (None, None), (0, None))\n", "\n", "\n", "def make_and_run_minuit(fcn, grad=None):\n", " m = Minuit(fcn, start_values, grad=grad, name=(\"amp\", \"mu\", \"sigma\"))\n", " m.errordef = Minuit.LIKELIHOOD\n", " m.limits = limits\n", " m.strategy = 0 # do not explicitly compute hessian after minimisation\n", " m.migrad()\n", " return m" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:38.532308Z", "start_time": "2020-02-21T10:26:38.368563Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 496.2 Nfcn = 66
EDM = 1.84e-08 (Goal: 0.0001)
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance APPROXIMATE
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 496.2 │ Nfcn = 66 │\n", "│ EDM = 1.84e-08 (Goal: 0.0001) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance APPROXIMATE │\n", "└──────────────────────────────────┴──────────────────────────────────────┘" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1 = make_and_run_minuit(nll)\n", "m1.fmin" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:39.371830Z", "start_time": "2020-02-21T10:26:38.797460Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 496.2 Nfcn = 26, Ngrad = 6
EDM = 1.84e-08 (Goal: 0.0001) time = 0.2 sec
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance APPROXIMATE
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 496.2 │ Nfcn = 26, Ngrad = 6 │\n", "│ EDM = 1.84e-08 (Goal: 0.0001) │ time = 0.2 sec │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance APPROXIMATE │\n", "└──────────────────────────────────┴──────────────────────────────────────┘" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2 = make_and_run_minuit(nll, grad=jax.grad(nll))\n", "m2.fmin" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:39.510553Z", "start_time": "2020-02-21T10:26:39.373728Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 496.2 Nfcn = 66
EDM = 1.84e-08 (Goal: 0.0001)
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance APPROXIMATE
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 496.2 │ Nfcn = 66 │\n", "│ EDM = 1.84e-08 (Goal: 0.0001) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance APPROXIMATE │\n", "└──────────────────────────────────┴──────────────────────────────────────┘" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m3 = make_and_run_minuit(jax.jit(nll))\n", "m3.fmin" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:40.573574Z", "start_time": "2020-02-21T10:26:40.229476Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 496.2 Nfcn = 26, Ngrad = 6
EDM = 1.84e-08 (Goal: 0.0001) time = 0.2 sec
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance APPROXIMATE
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 496.2 │ Nfcn = 26, Ngrad = 6 │\n", "│ EDM = 1.84e-08 (Goal: 0.0001) │ time = 0.2 sec │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance APPROXIMATE │\n", "└──────────────────────────────────┴──────────────────────────────────────┘" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m4 = make_and_run_minuit(jax.jit(nll), grad=jax.jit(jax.grad(nll)))\n", "m4.fmin" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 496.2 Nfcn = 82
EDM = 5.31e-05 (Goal: 0.0001) time = 1.0 sec
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance APPROXIMATE
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 496.2 │ Nfcn = 82 │\n", "│ EDM = 5.31e-05 (Goal: 0.0001) │ time = 1.0 sec │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance APPROXIMATE │\n", "└──────────────────────────────────┴──────────────────────────────────────┘" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from numba_stats import norm # numba jit-able version of norm\n", "\n", "@nb.njit\n", "def nb_nll(par):\n", " amp = par[0]\n", " mu, sigma = par[1:]\n", " p = norm.cdf(xe, mu, sigma)\n", " mu = amp * np.diff(p)\n", " result = np.sum(mu - n + n * np.log(n / (mu + 1e-323) + 1e-323))\n", " return result\n", "\n", "m5 = make_and_run_minuit(nb_nll)\n", "m5.fmin" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:45.031931Z", "start_time": "2020-02-21T10:26:40.674388Z" } }, "outputs": [], "source": [ "from timeit import timeit\n", "\n", "times = {\n", " \"no JIT, no grad\": \"m1\",\n", " \"no JIT, grad\": \"m2\",\n", " \"jax JIT, no grad\": \"m3\",\n", " \"jax JIT, grad\": \"m4\",\n", " \"numba JIT, no grad\": \"m5\",\n", "}\n", "for k, v in times.items():\n", " t = timeit(\n", " f\"{v}.values = start_values; {v}.migrad()\",\n", " f\"from __main__ import {v}, start_values\",\n", " number=1,\n", " )\n", " times[k] = t" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:26:45.142272Z", "start_time": "2020-02-21T10:26:45.033451Z" } }, "outputs": [ { "data": { "image/png": "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eXp6aty4cYqNjVW3bt0kSb1799batWs1btw41a9fP9PxJk2apIEDB6ply5aSpMmTJ2f6oPVy5crp7bffdmjr1auX/c8hISEaPny4unbtqvfff1/S/z3C6GooLV++vFavXq0lS5ZkuY+jRo3Sm2++mY3ZyLnjx4+rQ4cOOnLkiPz8/BQeHq6lS5eqYcOGkqSUlBS5uPzfv6fq1KmjTz75RIMGDdK///1vlStXTosWLbJ/VmbNmqVvvvlGmzZtkpubm9zc3DR79mzVrVtXTz31lBo3bpwn+wEAAO6iALp7924NHjxYSUlJOnHihP3IZ0pKij1UFC5cWB999JGio6NVp04dDRgwwJklOwgPD7f/+ep1hsePH1epUqWUnJyszp07O/R/9NFHNXHixEzHSk1N1bFjx1SrVi17m6urq6pXr26fl6syC+DLly/XqFGjtGPHDqWlpenKlSv6888/deHCBXl7eys5OdkebK+KiIi4aQAdOHCgevfubV9OS0tTcHBwlutk10cffZTl+wkJCde1Pffcc3ruuecy7d+hQwd16NDBoa1WrVr2634BAEDeuWtOwTdr1kynTp3StGnTlJSUpKSkJEm6LjD88MMPcnV11ZEjR3T+/HlnlJqpAgUK2P989U7rv4fFvODj4+OwfODAAT311FMKDw/X559/rg0bNui9996TdP1c5pSHh4f9V2VefQEAAPzdXRFAT548qZ07d2rQoEFq0KCBwsLCdPr06ev6rV69WmPGjNFXX30lX19fh+v97mRhYWFKTEx0aEtMTLzhMy79/PwUEBCgdevW2dvS09O1cePGm25rw4YNysjI0Pjx4/XII4+ofPny+t///nddPVcD/lU3+o1DAAAAOXVXnIIvXLiwihYtqqlTpyooKEgpKSnXnV4/e/as2rdvrx49eqhx48a6//77VbNmTTVr1kzPPvuskyrPnn79+un555/Xww8/rKioKH311VdasGCBli9ffsN1/vWvf2nUqFF68MEHVaFCBU2aNEmnT5++6XMsH3zwQV2+fFmTJk1Ss2bNHG5OuqpHjx569NFHNW7cODVv3lxLly696el3AACA7LorjoC6uLho7ty52rBhgypXrqxXX31VY8eOdejTs2dP+fj4aOTIkZKkKlWqaOTIkerSpYt+++23TMfNyMiQm9vtZfDY2Njbfih9ixYtNHHiRI0bN06VKlXShx9+qLi4uCzH7d+/v1q3bq0OHTooIiJCvr6+io6OlqenZ5bbqlq1qt555x2NGTNGlStX1pw5czRq1CiHPo888oimTZumiRMnqmrVqvruu+80aNCg29pHAACAq2zGGOPsIm6kdevWcnV11ezZs/Nk/NGjR2v27Nnatm3bLY9Rr1491a9fX0OHDs29wm5BRkaGwsLC9Pzzz2vYsGFOreWqtLQ0+fn5KbjXPLl43PqjsA6MbpqLVQEAAGe7I0/BX7lyRbt27dKaNWvUpUuXXB//woUL2rFjh+Li4m7rcTupqanau3evFi9enIvVZc/Bgwf13XffqV69erp48aImT56s/fv3q02bNpbXAgAAkBN35Cn4bdu2qUaNGqpUqZK6du2a6+NPnTpVUVFRqlq1qgYPHnzL4/j5+enw4cPy9fXNxeqyx8XFRfHx8apZs6YeffRRbd26VcuXL1dYWJjltQAAAOTEHX0KHnc3TsEDAIDM3JFHQAEAAHDvIoACAADAUgRQAAAAWIoACgAAAEsRQAEAAGApAigAAAAsRQAFAACApQigAAAAsBQBFAAAAJYigAIAAMBSBFAAAABYigAKAAAASxFAAQAAYCkCKAAAACxFAAUAAIClCKAAAACwFAEUAAAAliKAAgAAwFI2Y4xxdhG4N6WlpcnPz0+pqakqVKiQs8sBAAB3CI6AAgAAwFIEUAAAAFiKAAoAAABLEUABAABgKQIoAAAALEUABQAAgKUIoAAAALAUARQAAACWIoACAADAUgRQAAAAWIoACgAAAEsRQAEAAGApAigAAAAs5ebsAnDvqzxkqVw8vDN978DophZXAwAAnI0joAAAALAUARQAAACWIoACAADAUgRQAAAAWIoACgAAAEsRQAEAAGApAigAAAAsRQAFAACApQigAAAAsBQBFAAAAJYigAIAAMBSBFAAAABYigAKAAAASxFAAQAAYCkCKAAAACxFAAUAAIClCKAAAACwFAEUAAAAliKAwul++OEHNWvWTCVLlpTNZtOiRYuyvW5iYqLc3Nz00EMP5Vl9AAAgdxFA4XTnz59X1apV9d577+VovTNnzqhDhw5q0KBBHlUGAADyAgEUTte4cWMNHz5cLVu2zNF6Xbt2VZs2bRQREeHQ/vvvvyswMFAjR460t61evVru7u5asWJFrtQMAABuHQEUd6W4uDjt27dPQ4YMue694sWLa8aMGRo6dKjWr1+vs2fPqn379urevTtHSwEAuAPkywAaGxurFi1aOLuMu05Or8/MK7t379aAAQM0e/Zsubm5ZdqnSZMm6tSpk9q2bauuXbvKx8dHo0aNsrhSAACQmXwZQCdOnKj4+Pg8Gz8+Pl7+/v6ZLkdGRspms93wFRkZmWd13QvS09PVpk0bvfnmmypfvnyWfceNG6crV65o/vz5mjNnjjw8PCyqEgAAZCXzw0f3OD8/P6dte8GCBbp06ZIk6dChQ6pVq5aWL1+uSpUqSZLc3d3zbNvp6emy2Wxycbl7/91x9uxZrV+/Xps2bVL37t0lSRkZGTLGyM3NTd99952eeOIJSdLevXv1v//9TxkZGTpw4ICqVKnizNIBAMD/d/cmkdtw7Sn4JUuWqG7duvL391fRokX11FNPae/evfa+s2bNkq+vr3bv3m1v69atmypUqKALFy7keNtFihRRYGCgAgMDVbx4cUlS0aJF7W1FihTJ9lhffvmlypUrJ09PT9WvX18zZ86UzWbTmTNnJP3fkdcvv/xSFStWlIeHh1JSUrRu3To1bNhQxYoVk5+fn+rVq6eNGzc6jL179249/vjj8vT0VMWKFbVs2bIc72teKFSokLZu3arNmzfbX127dlVoaKg2b96s2rVrS5IuXbqkdu3aqVWrVho2bJg6duyo48ePO7l6AAAg5dMjoNc6f/68evfurfDwcJ07d06DBw9Wy5YttXnzZrm4uKhDhw76+uuv1bZtW61evVpLly7V9OnTtWbNGnl7ezut7v379+vZZ59Vz5491bFjR23atEl9+/a9rt+FCxc0ZswYTZ8+XUWLFlWJEiW0b98+xcTEaNKkSTLGaPz48WrSpIl2796tggULKiMjQ88884wCAgKUlJSk1NRU9erV66Y1Xbx4URcvXrQvp6WlZWtfzp07pz179jjs2+bNm1WkSBGVKlVKAwcO1G+//aZZs2bJxcVFlStXdli/RIkS8vT0dGh//fXXlZqaqv/85z/y9fXVN998oxdffFFff/11tmoCAAB5J98H0H/84x8OyzNmzFDx4sW1fft2e6D58MMPFR4erh49emjBggUaOnSoqlev7oxy7T788EOFhoZq7NixkqTQ0FBt27ZNI0aMcOh3+fJlvf/++6pataq97eop6qumTp0qf39/rVq1Sk89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l9a8efP00ksvObEy64wePVpz585VQkKCPD09nV3OHedG85Ofv3cKFiyozZs369y5c1qxYoV69+6tsmXLKjIy0tml3ZM4AoqbKlasmFxdXXXs2DGH9mPHjikwMDDTdQIDA7Psf/W/ORnzTpUX85OZsmXLqlixYtqzZ8/tF22RW5kbZ4zpLFbti7+/v8qXL59vPjvjxo3T6NGj9d133yk8PNzent+/d6660fxkJj9977i4uOjBBx/UQw89pD59+ujZZ5/VqFGjJN1bn507BQEUN+Xu7q7q1atrxYoV9raMjAytWLFCERERma4TERHh0F+Sli1bZu9fpkwZBQYGOvRJS0tTUlLSDce8U+XF/GTm8OHDOnnypIKCgnKncAvcytw4Y0xnsWpfzp07p7179+aLz87bb7+tYcOGacmSJapRo4bDe/n9e0fKen4yk5+/dzIyMnTx4kVJ99Zn547h7LugcHeYO3eu8fDwMPHx8Wb79u2mc+fOxt/f3xw9etQYY0z79u3NgAED7P0TExONm5ubGTdunElOTjZDhgzJ9DFM/v7+5osvvjC//PKLad68+V37SIvcnp+zZ8+avn37mjVr1pj9+/eb5cuXm2rVqply5cqZP//80yn7eKtyOjcXL140mzZtMps2bTJBQUGmb9++ZtOmTWb37t3ZHvNukhfz06dPH5OQkGD2799vEhMTTVRUlClWrJg5fvy45ft3O3I6N6NHjzbu7u7ms88+c3iM0NmzZx365NfvnZvNT37+3hk5cqT57rvvzN69e8327dvNuHHjjJubm5k2bZq9z7302bkTEECRbZMmTTKlSpUy7u7uplatWmbt2rX29+rVq2diYmIc+s+bN8+UL1/euLu7m0qVKpnFixc7vJ+RkWHeeOMNExAQYDw8PEyDBg3Mzp07rdiVPJGb83PhwgXz5JNPmuLFi5sCBQqY0qVLm06dOt2VAcuYnM3N/v37jaTrXvXq1cv2mHeb3J6fVq1amaCgIOPu7m7uu+8+06pVK7Nnzx4L9yj35GRuSpcunencDBkyxN4nP3/v3Gx+8vP3zuuvv24efPBB4+npaQoXLmwiIiLM3LlzHca71z47zmYzxhhrj7kCAAAgP+MaUAAAAFiKAAoAAABLEUABAABgKQIoAAAALEUABQAAgKUIoAAAALAUARQAAACWIoACAADAUgRQAMjHEhIS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", "text/plain": [ "

" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "\n", "x = np.fromiter(times.values(), dtype=float)\n", "xmin = np.min(x)\n", "\n", "y = -np.arange(len(times))\n", "plt.barh(y, x)\n", "for yi, k, v in zip(y, times, x):\n", " plt.text(v, yi, f\"{v/xmin:.1f}x\")\n", "plt.yticks(y, times.keys())\n", "for loc in (\"top\", \"right\"):\n", " plt.gca().spines[loc].set_visible(False)\n", "plt.xlabel(\"execution time / s\");" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Conclusions:\n", "\n", "1. As expected, the best results with JAX are obtained by JIT compiling function and gradient and using the gradient in the minimization. However, the performance of the Numba JIT compiled function is comparable even without computing the gradient.\n", "\n", "1. JIT compiling the cost function with JAX but not using the gradient also gives good performance, but worse than using Numba for the same.\n", "\n", "3. Combining the JAX JIT with the JAX gradient calculation is very important. Using only the Python-computed gradient even reduces performance in this example.\n", "\n", "In general, the gain from using a gradient is larger for functions with hundreds of parameters, as is common in machine learning. Human-made models often have less than 10 parameters, and then the gain is not so dramatic." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Computing covariance matrices with JAX\n", "\n", "Automatic differentiation gives us another way to compute uncertainties of fitted parameters. MINUIT compute the uncertainties with the HESSE algorithm by default, which computes the matrix of second derivates approximately using finite differences and inverts this.\n", "\n", "Let's compare the output of HESSE with the exact (within floating point precision) computation using automatic differentiation." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:27:38.715871Z", "start_time": "2020-02-21T10:27:37.907690Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sigma[amp] : HESSE = 100.0, JAX = 100.0\n", "sigma[mu] : HESSE = 0.0100, JAX = 0.0100\n", "sigma[sigma]: HESSE = 0.0071, JAX = 0.0071\n" ] } ], "source": [ "m4.hesse()\n", "cov_hesse = m4.covariance\n", "\n", "\n", "def jax_covariance(par):\n", " return jnp.linalg.inv(jax.hessian(nll)(par))\n", "\n", "\n", "par = np.array(m4.values)\n", "cov_jax = jax_covariance(par)\n", "\n", "print(\n", " f\"sigma[amp] : HESSE = {cov_hesse[0, 0] ** 0.5:6.1f}, JAX = {cov_jax[0, 0] ** 0.5:6.1f}\"\n", ")\n", "print(\n", " f\"sigma[mu] : HESSE = {cov_hesse[1, 1] ** 0.5:6.4f}, JAX = {cov_jax[1, 1] ** 0.5:6.4f}\"\n", ")\n", "print(\n", " f\"sigma[sigma]: HESSE = {cov_hesse[2, 2] ** 0.5:6.4f}, JAX = {cov_jax[2, 2] ** 0.5:6.4f}\"\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Success, HESSE and JAX give the same answer within the relevant precision.\n", "\n", "**Note:** If you compute the covariance matrix in this way from a least-squares cost function instead of a negative log-likelihood, you must multiply it by 2.\n", "\n", "Let us compare the performance of HESSE with Jax." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "30.4 ms ± 1.37 ms per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 3\n", "m = Minuit(nll, par)\n", "m.errordef = Minuit.LIKELIHOOD\n", "m.hesse()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "89 ms ± 28.6 ms per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 3\n", "jax_covariance(par)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The computation with Jax is slower, but it is also more accurate (although the added precision is not relevant).\n", "\n", "Minuit's HESSE algorithm still makes sense today. It has the advantage that it can process any function, while Jax cannot. Jax cannot differentiate a function that calls into C/C++ code or Cython code, for example.\n", "\n", "Final note: If we JIT compile `jax_covariance`, it greatly outperforms Minuit's HESSE algorithm, but that only makes sense if you need to compute the hessian at different parameter values, so that the extra time spend to compile is balanced by the time saved over many invocations. This is not what happens here, the Hessian in only needed at the best fit point." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "187 µs ± 28.1 µs per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 3 jit_jax_covariance = jax.jit(jax_covariance); jit_jax_covariance(par)\n", "jit_jax_covariance(par)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "It is much faster... but only because the compilation cost is excluded here." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "496 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 1\n", "# if we include the JIT compilation cost, the performance drops dramatically\n", "@jax.jit\n", "def jax_covariance(par):\n", " return jnp.linalg.inv(jax.hessian(nll)(par))\n", "\n", "\n", "jax_covariance(par)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "With compilation cost included, it is much slower.\n", "\n", "Conclusion: Using the JIT compiler makes a lot of sense if the covariance matrix has to be computed repeatedly for the same cost function but different parameters, but this is not the case when we use it to compute parameter errors." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Fit data points with uncertainties in x and y\n", "\n", "Let's say we have some data points $(x_i \\pm \\sigma_{x,i}, y_i \\pm \\sigma_{y,i})$ and we have a model $y=f(x)$ that we want to adapt to this data. If $\\sigma_{x,i}$ was zero, we could use the usual least-squares method, minimizing the sum of squared residuals $r^2_i = (y_i - f(x_i))^2 / \\sigma^2_{y,i}$. Here, we don't know where to evaluate $f(x)$, since the exact $x$-location is only known up to $\\sigma_{x,i}$.\n", "\n", "We can approximately extend the standard least-squares method to handle this case. We use that the uncertainty along the $x$-axis can be converted into an additional uncertainty along the $y$-axis with error propagation,\n", "\n", "$$\n", "f(x_i \\pm \\sigma_{x,i}) \\simeq f(x_i) \\pm f'(x_i)\\,\\sigma_{x,i}.\n", "$$\n", "\n", "Using this, we obtain modified squared residuals\n", "\n", "$$\n", "r^2_i = \\frac{(y_i - f(x_i))^2}{\\sigma^2_{y,i} + (f'(x_i) \\,\\sigma_{x,i})^2}.\n", "$$\n", "\n", "We demonstrate this with a fit of a polynomial." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:25:43.510168Z", "start_time": "2020-02-21T10:25:43.371319Z" } }, "outputs": [], "source": [ "# polynomial model\n", "def f(x, par):\n", " return jnp.polyval(par, x)\n", "\n", "\n", "# true polynomial f(x) = x^2 + 2 x + 3\n", "par_true = np.array((1, 2, 3))\n", "\n", "\n", "# grad computes derivative with respect to the first argument\n", "f_prime = jax.jit(jax.grad(f))\n", "\n", "\n", "# checking first derivative f'(x) = 2 x + 2\n", "assert f_prime(0.0, par_true) == 2\n", "assert f_prime(1.0, par_true) == 4\n", "assert f_prime(2.0, par_true) == 6\n", "# ok!\n", "\n", "# generate toy data\n", "n = 30\n", "data_x = np.linspace(-4, 7, n)\n", "data_y = f(data_x, par_true)\n", "\n", "rng = np.random.default_rng(seed=1)\n", "sigma_x = 0.5\n", "sigma_y = 5\n", "data_x += rng.normal(0, sigma_x, n)\n", "data_y += rng.normal(0, sigma_y, n)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:25:43.646212Z", "start_time": "2020-02-21T10:25:43.512384Z" } }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAioAAAGdCAYAAAA8F1jjAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA0J0lEQVR4nO3dfXSU9Z3//9ckkAkIMxhIMmG5iwUb02CR+xiwFqOBL/LVhdXaA1uCHD3NL1Ihu6ukqwYoEnAtsLoY1GUTu8jBulsVSg1irOnxW5BIyh5DBHULhZVMsKXMIGwmkMzvD5oxAwnJDLnmumbyfJwz5+S6rplr3lwkmVc+d5fN7/f7BQAAYEFxZhcAAADQGYIKAACwLIIKAACwLIIKAACwLIIKAACwLIIKAACwLIIKAACwLIIKAACwrD5mF3CtWltbdfLkSQ0cOFA2m83scgAAQDf4/X6dPXtWQ4cOVVxc5+0mUR9UTp48qeHDh5tdBgAACMOJEyc0bNiwTo9HfVAZOHCgpEv/UIfDYXI1AACgO7xer4YPHx74HO9M1AeVtu4eh8NBUAEAIMp0NWyDwbQAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyDA0qo0aNks1mu+JRWFgoSWpqalJhYaEGDx6sAQMGaN68eWpsbDSyJAAAEEUMDSo1NTVqaGgIPPbs2SNJuu+++yRJy5Yt086dO/X666+rurpaJ0+e1Ny5c40sCQAARBGb3+/3R+rNli5dql/+8pf67LPP5PV6lZycrG3btulv/uZvJEmHDx/WTTfdpL1792rq1KndOqfX65XT6ZTH42FlWgAAokR3P78jNkalublZW7du1YMPPiibzaYDBw7owoULys3NDTwnIyNDI0aM0N69ezs9j8/nk9frDXoAAIDYFLGg8uabb+rMmTPKz8+XJLndbiUkJGjQoEFBz0tNTZXb7e70PKWlpXI6nYEHd04GACB2RSyobNmyRbNmzdLQoUOv6TzFxcXyeDyBx4kTJ3qoQgAAYDURuXvyH/7wB7377rv6xS9+EdjncrnU3NysM2fOBLWqNDY2yuVydXouu90uu91uZLkAAESt880XlfnUbklS/ao89U+IyEe9YSLSolJeXq6UlBTNnj07sG/ChAnq27evqqqqAvuOHDmi48ePKzs7OxJlAQAAizM8ZrW2tqq8vFwLFy5Unz5fv53T6dTixYtVVFSkpKQkORwOLVmyRNnZ2d2e8QMAAGKb4UHl3Xff1fHjx/Xggw9ecWzDhg2Ki4vTvHnz5PP5lJeXpxdeeMHokgAAQJSI6DoqRmAdFQAAvhYtY1Qst44KAABAqAgqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADAsggqAADEkJbWr2/ht//o6aDtaERQAQAgRlTWNSh3fXVgO7+8RtPWvafKugYTq7o2BBUAAGJAZV2DCrbWqtHrC9rv9jSpYGtt1IYVa977GQCAXuh888WwXtfS6lfJjkPqqJPHL8kmacWOeuWMHqL4OFtI5+6fYG5UIKgAAGARmU/tNuS8fklub5PGrngn5NceWzu75wsKAV0/AADAsmhRAQDAIupX5YX1uv1HTyu/vKbL51UsmqTJ6UlhvYdZCCoAAFhEuONBpo9JVpozUW5PU4fjVGySXM5ETR+THPIYFbPR9QMAQJSLj7OpZE6mpEuhpL227ZI5mVEXUiSCCgAAMWFmVprKFoxXisMetN/lTFTZgvGamZVmUmXXhq4fAABixMysNOWMHhKY3VOxaFJUdve0R4sKAAAxpH0omZyeFNUhRSKoAAAACyOoAAAAyyKoAAAAyyKoAAAAyyKoAAAAyyKoAAAAyyKoAAAAyyKoAAAAyzI8qHzxxRdasGCBBg8erH79+mns2LH66KOPAsf9fr+eeuoppaWlqV+/fsrNzdVnn31mdFkAACAKGBpU/vznPysnJ0d9+/bV22+/rfr6ev30pz/V9ddfH3jOM888o+eee06bN2/Whx9+qOuuu055eXlqamoysjQAABAFDL3Xz7p16zR8+HCVl5cH9qWnpwe+9vv92rhxo5544gndc889kqSf/exnSk1N1ZtvvqkHHnjAyPIAAIDFGdqismPHDk2cOFH33XefUlJSdMstt+jll18OHD969Kjcbrdyc3MD+5xOp6ZMmaK9e/d2eE6fzyev1xv0AAAAscnQoPL73/9eZWVlGjNmjHbv3q2CggL96Ec/0iuvvCJJcrvdkqTU1NSg16WmpgaOXa60tFROpzPwGD58uJH/BAAAYCJDg0pra6vGjx+vNWvW6JZbbtHDDz+shx56SJs3bw77nMXFxfJ4PIHHiRMnerBiAABgJYaOUUlLS1NmZmbQvptuukn/+Z//KUlyuVySpMbGRqWlpQWe09jYqHHjxnV4TrvdLrvdbkzBAABEuf4JfXRs7Wyzy+gxhrao5OTk6MiRI0H7Pv30U40cOVLSpYG1LpdLVVVVgeNer1cffvihsrOzjSwNAICQnW++qFHLd2nU8l0633zR7HJ6BUNbVJYtW6Zbb71Va9as0f3336/9+/frpZde0ksvvSRJstlsWrp0qVavXq0xY8YoPT1dTz75pIYOHap7773XyNIAAEAUMDSoTJo0SW+88YaKi4u1atUqpaena+PGjZo/f37gOY899pjOnTunhx9+WGfOnNG0adNUWVmpxMREI0sDACDgfPNFZT61W5JUvypP/RMM/XiMunrMZPi//O6779bdd9/d6XGbzaZVq1Zp1apVRpcCAACiDPf6AQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQCgm1pa/YGv9x89HbQNYxBUAADohsq6BuWurw5s55fXaNq691RZ12BiVbGPoAIAQBcq6xpUsLVWjV5f0H63p0kFW2sJKwbqY3YBAABEwvnmi50eO9t0IfD1bz79Ujmjhyg+zibpUndPyY5D6qiTxy/JJmnFjvqg14Sif8KVH8WXdzFNH5Mc1rljgc3v90d1B5vX65XT6ZTH45HD4TC7HACARY1avsvsEjp0bO3soO3KugaV7DgU1HqT5kxUyZxMzcxKi3R5hunu5zddPwAAWARdTFei6wcAENWu1qXT3kdP3BG03dLq15zn/59OnfV18gop1WFXyZxM/X+v/q7L829eMF4TR12v/21ukST1S4jvVl1t9ZvRxRQN6PoBAEQ1q3bpWM3lXUxmo+sHAABEvehsBwIA4C/qV+WF9br9R08rv7ymy+dVLJqkyelJ2lPfqKd3fRLUVeRyJKr4/2TozszUwL62rpxQu1pCrae3IKgAAKJauGMvpo9JVpozUW5PU4fjQmySXM7EwNTge8b9lWZkpGjsinckXQoMHU0bjlQ9vQVdPwCAXik+zqaSOZmSLoWA9tq2S+ZkBoWC9l9PTk/q0cAQTj29AUEFANBrzcxKU9mC8Upx2IP2u5yJKlswPuLrllitHiug6wcA0KvNzEpTzughXXbp9NZ6zEaLCgCg1zOySyccVqvHTAQVAABgWQQVAABgWQQVAABgWQQVAABgWYYGlRUrVshmswU9MjIyAsebmppUWFiowYMHa8CAAZo3b54aGxuNLAkAAEQRw1tUvvWtb6mhoSHw+OCDDwLHli1bpp07d+r1119XdXW1Tp48qblz5xpdEgAAiBKGr6PSp08fuVyuK/Z7PB5t2bJF27Zt04wZMyRJ5eXluummm7Rv3z5NnTrV6NIAAIDFGd6i8tlnn2no0KG64YYbNH/+fB0/flySdODAAV24cEG5ubmB52ZkZGjEiBHau3ev0WUBAIAoYGiLypQpU1RRUaFvfvObamho0MqVKzV9+nTV1dXJ7XYrISFBgwYNCnpNamqq3G53p+f0+Xzy+b6+c6XX6zWqfAAAYDJDg8qsWbMCX998882aMmWKRo4cqZ///Ofq169fWOcsLS3VypUre6pEAABgYRGdnjxo0CDdeOON+vzzz+VyudTc3KwzZ84EPaexsbHDMS1tiouL5fF4Ao8TJ04YXDUAADBLRIPKV199pf/+7/9WWlqaJkyYoL59+6qqqipw/MiRIzp+/Liys7M7PYfdbpfD4Qh6AAAQCf0T+ujY2tk6tna2+idwX99IMPQq//3f/73mzJmjkSNH6uTJkyopKVF8fLy+//3vy+l0avHixSoqKlJSUpIcDoeWLFmi7OxsZvwAAABJBgeV//mf/9H3v/99/elPf1JycrKmTZumffv2KTk5WZK0YcMGxcXFad68efL5fMrLy9MLL7xgZEkAAFheW8sNJJvf7/ebXcS18Hq9cjqd8ng8dAMBwF+cb76ozKd2S5LqV+XRTQHL6e7nN/f6AQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQAYAY1NLqD3y9/+jpoG0gmhBUACDGVNY1KHd9dWA7v7xG09a9p8q6BhOrAsJDUAGAGFJZ16CCrbVq9PqC9rs9TSrYWktYQdTpY3YBAIBg55svhvW6lla/SnYcUkedPH5JNkkrdtQrZ/QQxcfZQjp3/wQ+LmAOvvMAwGIyn9ptyHn9ktzeJo1d8U7Irz22dnbPFwR0A10/AIAecb75okYt36VRy3eF3SoEXI4WFQCwmPpVeWG9bv/R08ovr+nyeRWLJmlyelJY7wFEGkEFACwm3PEg08ckK82ZKLenqcNxKjZJLmeipo9JDnmMCmAWun4AIEbEx9lUMidT0qVQ0l7bdsmcTEIKogpBBQBiyMysNJUtGK8Uhz1ov8uZqLIF4zUzK82kyoDw0PUDADFmZlaackYPCczuqVg0ie4eRC1aVAAgBrUPJZPTkwgpiFoEFQAAYFkEFQAAYFkEFQAAYFkEFQAAYFkRCypr166VzWbT0qVLA/uamppUWFiowYMHa8CAAZo3b54aGxsjVRIAALC4iASVmpoavfjii7r55puD9i9btkw7d+7U66+/rurqap08eVJz586NREkAACAKGB5UvvrqK82fP18vv/yyrr/++sB+j8ejLVu2aP369ZoxY4YmTJig8vJy/fa3v9W+ffuMLgsAAEQBw4NKYWGhZs+erdzc3KD9Bw4c0IULF4L2Z2RkaMSIEdq7d2+n5/P5fPJ6vUEPAAAQmwxdmXb79u2qra1VTc2Vd/N0u91KSEjQoEGDgvanpqbK7XZ3es7S0lKtXLmyp0sFAAAWZFiLyokTJ/Too4/q1VdfVWJiYo+dt7i4WB6PJ/A4ceJEj50bABC+ltav79m8/+jpoG0gXIYFlQMHDujUqVMaP368+vTpoz59+qi6ulrPPfec+vTpo9TUVDU3N+vMmTNBr2tsbJTL5er0vHa7XQ6HI+gBADBXZV2DctdXB7bzy2s0bd17qqxrMLEqxALDgsodd9yhjz/+WAcPHgw8Jk6cqPnz5we+7tu3r6qqqgKvOXLkiI4fP67s7GyjygIA9LDKugYVbK1Vo9cXtN/taVLB1tqQw8r55osatXyXRi3fpfPNF3uyVEQhw8aoDBw4UFlZWUH7rrvuOg0ePDiwf/HixSoqKlJSUpIcDoeWLFmi7OxsTZ061aiyAACdCCcUtLT6VbLjkDrq5PFLsklasaNeOaOHdPvGiIQTtGfoYNqubNiwQXFxcZo3b558Pp/y8vL0wgsvmFkSAPRamU/t7vFz+iW5vU0au+KdHj83egeb3++P6tFOXq9XTqdTHo+H8SoAcA1GLd9ldglXqF+Vp/4Jpv5NDYN09/Ob/30AgKRLoSBU+4+eVn75lUtQXK5i0SRNTk/q1jnPN1/UxNVVXT8RvQJBBQAgSWG1XEwfk6w0Z6LcnqYOx6nYJLmciZo+JrnbY1SA9rh7MgAgbPFxNpXMyZR0KZS017ZdMieTkIKwEVQAANdkZlaayhaMV4rDHrTf5UxU2YLxmpmVZlJliAV0/QAArtnMrDTljB4SmN1TsWgS3T3oEbSoAAB6RPtQMjk9ybCQwoJwvQtBBQAAWBZBBQAAWBZBBQAAWBZBBQAAWBZBBQAAWBZBBQAAWBZBBQBgKS2tXy/Gv//o6aBt9D4EFQCAZVTWNSh3fXVgO7+8RtPWvafKugYTq4KZCCoAAEuorGtQwdZaNXp9QfvdniYVbK0lrPRSLKEPAOhxoa4Y29LqV8mOQx3egdmvSzc4XLGjXjmjh8h3sSWk9wnnrtCwDpvf74/qzj+v1yun0ymPxyOHw2F2OQDQa51vvqjMp3abXcYVjq2dbXYJ6EB3P7/p+gEAAJZFexgAoMd99MQdIXW57D96WvnlNV0+r2LRJGX9lUMTV1eF9T6IPvzvAgB6XP+EPiEFiOljkpXmTJTb09ThOBWbJJczUdPHJAeNUQn1fRB96PoBAPSI/gl9dGztbB1bOzvk8BAfZ1PJnExJl0JJe23bJXMyFR93+VHEOoIKAMASZmalqWzBeKU47EH7Xc5ElS0Yr5lZaZJYEK63YdYPAMBSzjZd0NgV70i6NCZl+pjkQEtKZV2DSnYcClprJc2ZqJI5mYEgg+jArB8AQFRq370zOT0pKKSwIFzvwwgkAEBEdbVIW/vjbV+HsiBcOONYGJBrXXT9AAAiatTyXWaXcAUWhYs8un4AAEDUo60LABBR9avyrnr8fPPFKxZ0C2VBuMnpST1SJ6yBoAIAiKhQxoO0LegWyoJwrLUSW+j6AQBYHgvC9V4EFQBAVOjugnCILYYGlbKyMt18881yOBxyOBzKzs7W22+/HTje1NSkwsJCDR48WAMGDNC8efPU2NhoZEkAgCg2MytN7xZ9J7BdsWiSPnh8BiElhhkaVIYNG6a1a9fqwIED+uijjzRjxgzdc889OnTokCRp2bJl2rlzp15//XVVV1fr5MmTmjt3rpElAQCiXGcLwiE2GTqYds6cOUHbTz/9tMrKyrRv3z4NGzZMW7Zs0bZt2zRjxgxJUnl5uW666Sbt27dPU6dONbI0AAAQBSI2RqWlpUXbt2/XuXPnlJ2drQMHDujChQvKzc0NPCcjI0MjRozQ3r17Oz2Pz+eT1+sNegAAgNhkeFD5+OOPNWDAANntdv3whz/UG2+8oczMTLndbiUkJGjQoEFBz09NTZXb7e70fKWlpXI6nYHH8OHDDf4XAAAAsxgeVL75zW/q4MGD+vDDD1VQUKCFCxeqvr4+7PMVFxfL4/EEHidOnOjBagEgupxvvqhRy3dp1PJdXd5DB4hGhi/4lpCQoNGjR0uSJkyYoJqaGv3zP/+zvve976m5uVlnzpwJalVpbGyUy+Xq9Hx2u112u73T4wAAIHZEfB2V1tZW+Xw+TZgwQX379lVVVVXg2JEjR3T8+HFlZ2dHuiwAiAhaQLrWP6GPjq2drWNrZ3NXYxjbolJcXKxZs2ZpxIgROnv2rLZt26b3339fu3fvltPp1OLFi1VUVKSkpCQ5HA4tWbJE2dnZzPgBAACSDA4qp06d0g9+8AM1NDTI6XTq5ptv1u7du3XnnXdKkjZs2KC4uDjNmzdPPp9PeXl5euGFF4wsCQAARBFDg8qWLVuuejwxMVGbNm3Spk2bjCwDAABEKe71AwAALIugAgAALIugAgAALIugAgAxjinRiGZMUAcARJW2dVbQO9CiAgAALIugAgAALIugYjD6hgEACB9BBQAAWBZBBQAAWBZBBQCiWEurP/D1/qOng7aBWEBQAYAoVVnXoNz11YHt/PIaTVv3nirrGkysCuhZBBUAiKCeagGprGtQwdZaNXp9QfvdniYVbK0lrCBmsOAbAERIZV2DSnYcCmznl9co1WHXj//PTbozM7Xb52lp9atkxyF1FHH8kmySVuyoV87oIYqPswXNOOzO7MP+CXw0wDr4bgSACGhrAbk8XDR6fXp0+8EefS+/JLe3SWNXvHPFsYmrq7p8Pau+wkoIKgDQzvnmi8p8arckqX5VXlDrQrhrIV2tBQTA1RFUAKCb2gJMtKhYNEmT05N0vvlioCXloyfuoGsHUYXvVgCIMTZJLmeipo9JVnycLehY/4Q+BBVEFb5bAaCb6lflhfW6/UdPK7+8psvntbWAdMee+kYt3X7wiu6ktlhSMifzipACRCOCCgB0U7gtEdPHJCvNmSi3p6nDcSpXawHpzD3j/kr2PnEq2XEoaIqyy5mokjmZmpmVFth3+ZToUN4HMBvrqACAweLjbCqZkynp6xaPNtfSAjIzK03vFn0nsF2xaJI+eHxGUEhhUThEO4IKAETAzKw0lS0YrxSHPWi/y5mosgXjg8JFKNqHm8npSUHbLAqHWEDXDwBEyMysNOWMHhJY36Ri0aQuu2G6mhLd2WJuoS4KFwoG4yKS+G4zGH3DANq7WgtIR0KZEt2dxdzaXG1RuK6wIBwiia4fA9E3DADAtaFFxSCdLZfd1jd8LX3SAHqPrqZEd7aYmxFTogEzEFS6EM6S2Ub2DUv0DwO9SSg/7+0XczNiSjRgBj7xumDEktnX0jcs0T8MoGttU6ILttbKJgWFFRaFQzRhjAoAxCijpkQDkUSLShfCWTKbvmEAVhHOlGjASgxtUSktLdWkSZM0cOBApaSk6N5779WRI0eCntPU1KTCwkINHjxYAwYM0Lx589TY2GhkWSFp6/MN5dHWN9zZrwGbpLS/9A2Hc34ACEWoU6IBKzE0qFRXV6uwsFD79u3Tnj17dOHCBd111106d+5c4DnLli3Tzp079frrr6u6ulonT57U3LlzjSzLcEYtlw3AeJevfdR+G0DkGfrneWVlZdB2RUWFUlJSdODAAd12223yeDzasmWLtm3bphkzZkiSysvLddNNN2nfvn2aOnWqkeUZqq1vuDs3DGvvfPPFwADe+lV5tKAAEVRZ16CSHYcC2/nlNUrr4mcWgLEiOpjW4/FIkpKSLo3LOHDggC5cuKDc3NzAczIyMjRixAjt3bs3kqUZojs3DANgDdwXB7CmiP253traqqVLlyonJ0dZWVmSJLfbrYSEBA0aNCjouampqXK73R2ex+fzyef7+heJ1+s1rOaeQN8wEDnhrHsksfYRYGUR++kpLCxUXV2dPvjgg2s6T2lpqVauXNlDVQGIJUaseySx9hFgpoh0/TzyyCP65S9/qV//+tcaNmxYYL/L5VJzc7POnDkT9PzGxka5XK4Oz1VcXCyPxxN4nDhxwsjSAcDS+if00bG1s3Vs7WxabhCTDP2u9vv9WrJkid544w29//77Sk9PDzo+YcIE9e3bV1VVVZo3b54k6ciRIzp+/Liys7M7PKfdbpfdbu/wGIDeLZx1jyTWPgKszNCgUlhYqG3btumtt97SwIEDA+NOnE6n+vXrJ6fTqcWLF6uoqEhJSUlyOBxasmSJsrOzo3rGDwBzhNuiEMn74rS1gADoHkO7fsrKyuTxeHT77bcrLS0t8HjttdcCz9mwYYPuvvtuzZs3T7fddptcLpd+8YtfGFkWAARh7SPAugzv+ulKYmKiNm3apE2bNhlZCgBcVbhrHwEwFiOvAOAvuC8OYD3cPRkA2mHtI8BaCCoAAMCyCCoAAMCyGKMCADGOKdGIZrSoAAAAyyKoAAAAyyKoWExL69drz+w/ejpoGwCA3oYxKgYLpW+4sq5BJTsOBbbzy2uUxmJTQNjON18M3FG5flUeN+0DohAtKhZRWdeggq21QStiSpLb06SCrbWqrGswqTIgdp1vvqhRy3dp1PJdOt980exyAHSAPy96ULi/6Fpa/SrZcajDm6H5deleIyt21Ctn9JCwFp/ir0gAQLTiE6wHtTUx9zS/JLe3KbCsd6iYlggAiFZ0/QAAAMuiRaUH1a/KC+t1+4+eVn55TZfPq1g0SZPTk8J6DwAAohFBpQeFOxZk+phkpTkT5fY0dThOxaZLt5rnLq4AgN6Grh8LiI+zqWROpqRLoaS9tu2SOZmEFABAr0NQsYiZWWkqWzBeKQ570H6XM1FlC8azjgrQDT0x3bht7aNja2czYw6wAH4KLWRmVppyRg8JzO6pWDSJ7h4AQK9Gi4rFtA8lk9OTCCkAgF6NoAIAACyLoAIAACyLoAIAACyLoAIAACyLoAIAACyLoAIgZrW0fr3W8/6jp4O2AUQHggoiqicW5AK6Y099o3LXVwe288trNG3de6qsawjsI8gA1seCbwBi0tLtB6+4d5bb06SCrbUqWzBeklSy41DgWH55jdKciSqZk8lK0ICFEFQAWFI4LW7tX9NR24hfl+6ftfw/P5bnfy90GmQ2PjBOd2amdvgeLKsPRBY/cQCuyfnmi8p8arckqX5VXo99kLeds6f5JZ353wudHpOkR7cf7PT19avyrnp+ggzQs/iJgiUY9WEH9LSuAtSxtbMjVAnQO/BpAMCSumq5MKrFBYC1GBpUfvOb3+if/umfdODAATU0NOiNN97QvffeGzju9/tVUlKil19+WWfOnFFOTo7Kyso0ZswYI8sCEAW6alXrKMicb76oiaureuT9KxZN0uT0pB45F4DwGTo9+dy5c/r2t7+tTZs2dXj8mWee0XPPPafNmzfrww8/1HXXXae8vDw1NTUZWRaAGNA/oc8VD3uf+KDnXH7v8bbtQf37XnGs/XPSnImaPia5w/fo6gGgZxn6UzVr1izNmjWrw2N+v18bN27UE088oXvuuUeS9LOf/Uypqal688039cADDxhZmmX1T+hDHzcQhsq6hqDpxpJks0n+dlN7XH+ZfixJBVtrZVPw7KC28FIyJ1PxcZ1FGQCRZFr8P3r0qNxut3JzcwP7nE6npkyZor1793YaVHw+n3w+X2Db6/UaXisAa6usa1DB1torphu3X7+tYtEkTR+THAggZQvGq2THITV6v/594mIdFcByTAsqbrdbkpSaGrxWQWpqauBYR0pLS7Vy5UpDa4t2zKBBNAp3peKWVr9KdhzqcN2U9m5KGyjfxZbA9m03JmvHIzmasuY9SdLmBeOVM3qI4uNsQbXw8wOYK+p+AouLi1VUVBTY9nq9Gj58uIkVAegJRs/iaQsknfnh1toO99MVC5jLtHv9uFwuSVJjY2PQ/sbGxsCxjtjtdjkcjqAHAACITaa1qKSnp8vlcqmqqkrjxo2TdKl15MMPP1RBQYFZZQEwSVfrpnRm/9HTyi+v6fJ5mxeM1203Jgftaz+d+aMn7qCbB7AgQ38qv/rqK33++eeB7aNHj+rgwYNKSkrSiBEjtHTpUq1evVpjxoxRenq6nnzySQ0dOjRorRUAnYul8Ujh1j59TLLSnIlye5quOk4lZ/SQq74H04sBazL0p/Kjjz7Sd7/73cB229iShQsXqqKiQo899pjOnTunhx9+WGfOnNG0adNUWVmpxMREI8sCEEPi42wqmZPZ6XRjf7vnAYg+ho5Ruf322+X3+694VFRUSJJsNptWrVolt9utpqYmvfvuu7rxxhuNLAkma2k3X3T/0dNB20C4ZmalqWzBeKU47EH7Ux380QNEO9MG06JnnG++qFHLd2nU8l1hT++MlMq6BuWurw5s55fXaNq691RZ12BiVYgVM7PS9G7RdwLbFYsmaU/RbSZWBKAnEFQQEW0LcrVfXEuS3J4mFWyt1Z76xk5eCXRf++6dyelJdPcAMYCRY+g2Ixbk8uvSOII1v/ok7PeJ1ADIWBq42pMu785rv/orAFwrftOi24xakMsvBbW0hHr3WysvyBXr4eby++vkl9cojWXoAfQgun4AhKWr7jzGHgHoCbH15x0MZfSCXO21dRxsfGCc7sxMvepzET4ju/NW7KgP3DsnVLHW8gQgfPw2QLd19uHR1YfdhJHXK9Vh1ymvr8sbx7Vp+7Ar/dVhzchIueqHXWfvz4dd14zsznN7mzR2xTthvd7K3XkAIovf5LhmfNgBAIxCUAF6MaO78yoWTdLk9KSw3qMn9E/oQ2AFohxBJQZFerpoKB92Z5suaMqa9yRJy3LHaMO7n3X5GrM/7GKZUffXsUlyORMtP1WZIANYH7N+YowZq7+23cytO4+BiX0Dr3twWrrSnInq7GPMJintLx92obwHN5frWR2tftx2fx1JV/z/tW2XzMm0dEgBEB0IKjFkT31jVE0X5cMuunV2fx2XM1FlC8azjgqAHsGfnRYT6nTR9s9/etcnUTddtO3DrmTHoaCA5WLRsKgwMytNOaOHBAY8VyyaZPnuHgDRhaBiMdcyg+bUWV+nx6w8g4YPu/BZYfl67q8DwEh0/cA0mU/tDox74MMudKGMR4qmu2wDQHu0qFhMqNNFzzdfDOneOMygiQ1ty9df3tXXNh6pt44RYRYPEHsIKhYT6liQ9k3/1/fvqzPnL0T1dNHeJJLL17d/r67elxlTAKyE30hR7PI71/75/IUOn8cMGmsya0XfrlrgaJEAYCWMUYlSnd25tiNMF+0Zlw9cbb8NADAGLSomMqLp/3KbF4wPdAF09/1o+r/S5a1X+eU1SuvGFOqrzcqJ5PL17ccyffTEHfwfA4ga/LYykVFN/+39cGttyK+h6T9YuANXuwo3Zi1fz8q9AKIJXT/oNc43Xwz5cbbpwlUHrkqXBq6ebboQ9Lq3Dn5x1VWC3zr4RdgtaqzoC6A34c8qExnd9C/RzN+eES1YoS6k1xZuHt1+UFJ43wP9E/qwoi+AXoNPMBMZ2fTftp9mfmsLJzy1dc2xoi+A3oBPsCjU1vRfsLU2KJRIV3YF4GvhtF5EYhzRtWBFXwCxjqASpa7W9L98Vkaga8Fq2lYOPd98MeIhIJyWpY9X3KXc9dU65fV12nqV6kjUnqLbAiEhnFk5AICOEVSiWGdN/76LLSZXFjsGJvbVyv/7rau2Xq34v5kamNg3sP9aZ+VYjRVufAig92LWT5Sj6d94ba1XKQ570P7OFtKLpVk53bnxYVsr2bG1sxkPBaDHEVSAbpiZlaZ3i74T2K5YNEkfPD6j09k1oYYbK+ps9eO2KdYd3aUZAHoaf/7AEqLhrrehtl6FMiun/Zid+lV5PdYy0bZWSyg3JZTCu/FhKGh5AdBd/LaAKXrLuAezu+Y6GrDc1U0JuyPU9WMuZ/VQCsA6LNH1s2nTJo0aNUqJiYmaMmWK9u/fb3ZJMFB3xj0AACBZoEXltddeU1FRkTZv3qwpU6Zo48aNysvL05EjR5SSkmJ2eehh4d43B+H55wfGaen2g1dc77Z2nY0PjNOdmalXvI4p1gCswvSgsn79ej300ENatGiRJGnz5s3atWuX/u3f/k3Lly83uTp0xIi7PjPuoXNXu95XG3vS0urXml99ctXrXfqrw5qRkXLF9Z4w8nqlOuxXXT8mmqZYA4hepv5mb25u1oEDB1RcXBzYFxcXp9zcXO3du7fD1/h8Pvl8X89C8Hq9hteJYEYt1Ma4h45193qHOvbkWq63X9EzxRpAdDM1qPzxj39US0uLUlODm55TU1N1+PDhDl9TWlqqlStXRqK8qBUNM2gQ/eiiAxAJUddWXlxcrKKiosC21+vV8OHDTayo9zH6rs+Mewh2tet9vvlioCXl8jtl99T1Ptt0QVPWvCdJ2rxgfNhdcwAQDlODypAhQxQfH6/Gxsag/Y2NjXK5XB2+xm63y263d3gMkWHkXZ8Z93Cl7l7vy++UbcT1vu3G5JgdCwTAmkydnpyQkKAJEyaoqurrvvXW1lZVVVUpOzvbxMpghFhaWj4acL0BxALT11EpKirSyy+/rFdeeUWffPKJCgoKdO7cucAsIMSWWFha3kq6us8O1xtAtDO9Dfd73/uevvzySz311FNyu90aN26cKisrrxhgi9gRytLykWDU8vVWYbXrDQChsMRv5EceeUSPPPKI2WUggsxeWr634XoDiFaWCCoIH1ORAQCxzPQxKgAAAJ2hRQXoJlqvACDyCCqAgbobblpav17pZP/R0wx2BYC/oOsHMFllXYNy11cHtvPLazRt3XuqrGswsSoAsAaCCmCiyroGFWytVaPXF7Tf7WlSwdZawgqAXo+uH+AanW++GNbrWlr9KtlxqMPl7f26tHrsih31Yd9bJ9bWgwHQO/GbDLhGbYvF9TS/JLe3KbBQW6gY+AsgFtD1AwAALIsWFeAa1a/KC+t1+4+eVn55TZfPq1g0SZPTk8J6DwCIdgQV4BqFOxZk+phkpTkT5fY0dThOxaZLNw80e6oy68cAMBNdP4BJ4uNsKpmTKelSKGmvbbtkTibrqQDo1QgqgIlmZqWpbMF4pTjsQftdzkSVLRivmVlpJlUGANZA1w9gsplZacoZPSQwu6di0STTu3sAwCoIKjCFlcY9WGH5+vbvNzk9qcff30rXGwBCQdcPejWWrwcAayOooNdi+XoAsD66fhDVWL4eAGIbv00R1Vi+HgBiG10/AADAsmhRQVRj+XoAiG0EFUS1WF++HgB6O7p+0CuxfD0ARAeCCnotlq8HAOuj6we9GsvXA4C10aKCXs/o5esBAOEjqAAAAMsiqAAAAMsiqAAAAMtiMC1gAf0T+rDsPgB0gBYVAABgWYYFlaefflq33nqr+vfvr0GDBnX4nOPHj2v27Nnq37+/UlJS9A//8A+6eDG8u+ECAIDYY1jXT3Nzs+677z5lZ2dry5YtVxxvaWnR7Nmz5XK59Nvf/lYNDQ36wQ9+oL59+2rNmjVGlQUAAKKIYS0qK1eu1LJlyzR27NgOj7/zzjuqr6/X1q1bNW7cOM2aNUs/+clPtGnTJjU3NxtVFgAAiCKmjVHZu3evxo4dq9TU1MC+vLw8eb1eHTp0qNPX+Xw+eb3eoAcAAIhNpgUVt9sdFFIkBbbdbnenrystLZXT6Qw8hg8fbmidAADAPCEFleXLl8tms131cfjwYaNqlSQVFxfL4/EEHidOnDD0/QAAgHlCGkz7d3/3d8rPz7/qc2644YZuncvlcmn//v1B+xobGwPHOmO322W32zs9DgAAYkdIQSU5OVnJyck98sbZ2dl6+umnderUKaWkpEiS9uzZI4fDoczMzB55DwAAEN0Mm558/PhxnT59WsePH1dLS4sOHjwoSRo9erQGDBigu+66S5mZmfrbv/1bPfPMM3K73XriiSdUWFhIiwkiilVhAcC6bH6/32/EifPz8/XKK69csf/Xv/61br/9dknSH/7wBxUUFOj999/Xddddp4ULF2rt2rXq06f7+cnr9crpdMrj8cjhcPRU+QAAwEDd/fw2LKhECkEFAIDo093Pb+71AwAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALIugAgAALKuP2QVcq7abP3u9XpMrAQAA3dX2ud32Od6ZqA8qZ8+elSQNHz7c5EoAAECozp49K6fT2elxm7+rKGNxra2tOnnypAYOHCibzWZqLV6vV8OHD9eJEyfkcDhMrcXquFah4XqFhuvVfVyr0HC9uq+ra+X3+3X27FkNHTpUcXGdj0SJ+haVuLg4DRs2zOwygjgcDr6Bu4lrFRquV2i4Xt3HtQoN16v7rnatrtaS0obBtAAAwLIIKgAAwLIIKj3IbrerpKREdrvd7FIsj2sVGq5XaLhe3ce1Cg3Xq/t66lpF/WBaAAAQu2hRAQAAlkVQAQAAlkVQAQAAlkVQAQAAlkVQMZjP59O4ceNks9l08OBBs8uxpGPHjmnx4sVKT09Xv3799I1vfEMlJSVqbm42uzRL2LRpk0aNGqXExERNmTJF+/fvN7skSyotLdWkSZM0cOBApaSk6N5779WRI0fMLisqrF27VjabTUuXLjW7FMv64osvtGDBAg0ePFj9+vXT2LFj9dFHH5ldliW1tLToySefDPqd/pOf/KTLe/p0hqBisMcee0xDhw41uwxLO3z4sFpbW/Xiiy/q0KFD2rBhgzZv3qwf//jHZpdmutdee01FRUUqKSlRbW2tvv3tbysvL0+nTp0yuzTLqa6uVmFhofbt26c9e/bowoULuuuuu3Tu3DmzS7O0mpoavfjii7r55pvNLsWy/vznPysnJ0d9+/bV22+/rfr6ev30pz/V9ddfb3ZplrRu3TqVlZXpX/7lX/TJJ59o3bp1euaZZ/T888+Hd0I/DPOrX/3Kn5GR4T906JBfkv93v/ud2SVFjWeeecafnp5udhmmmzx5sr+wsDCw3dLS4h86dKi/tLTUxKqiw6lTp/yS/NXV1WaXYllnz571jxkzxr9nzx7/d77zHf+jjz5qdkmW9Pjjj/unTZtmdhlRY/bs2f4HH3wwaN/cuXP98+fPD+t8tKgYpLGxUQ899JD+/d//Xf379ze7nKjj8XiUlJRkdhmmam5u1oEDB5SbmxvYFxcXp9zcXO3du9fEyqKDx+ORpF7/fXQ1hYWFmj17dtD3GK60Y8cOTZw4Uffdd59SUlJ0yy236OWXXza7LMu69dZbVVVVpU8//VSS9F//9V/64IMPNGvWrLDOF/U3JbQiv9+v/Px8/fCHP9TEiRN17Ngxs0uKKp9//rmef/55Pfvss2aXYqo//vGPamlpUWpqatD+1NRUHT582KSqokNra6uWLl2qnJwcZWVlmV2OJW3fvl21tbWqqakxuxTL+/3vf6+ysjIVFRXpxz/+sWpqavSjH/1ICQkJWrhwodnlWc7y5cvl9XqVkZGh+Ph4tbS06Omnn9b8+fPDOh8tKiFYvny5bDbbVR+HDx/W888/r7Nnz6q4uNjskk3V3evV3hdffKGZM2fqvvvu00MPPWRS5Yh2hYWFqqur0/bt280uxZJOnDihRx99VK+++qoSExPNLsfyWltbNX78eK1Zs0a33HKLHn74YT300EPavHmz2aVZ0s9//nO9+uqr2rZtm2pra/XKK6/o2Wef1SuvvBLW+VhCPwRffvml/vSnP131OTfccIPuv/9+7dy5UzabLbC/paVF8fHxmj9/ftj/WdGmu9crISFBknTy5Endfvvtmjp1qioqKhQX17tzdHNzs/r376//+I//0L333hvYv3DhQp05c0ZvvfWWecVZ2COPPKK33npLv/nNb5Senm52OZb05ptv6q//+q8VHx8f2NfS0iKbzaa4uDj5fL6gY73dyJEjdeedd+pf//VfA/vKysq0evVqffHFFyZWZk3Dhw/X8uXLVVhYGNi3evVqbd26NazWYLp+QpCcnKzk5OQun/fcc89p9erVge2TJ08qLy9Pr732mqZMmWJkiZbS3eslXWpJ+e53v6sJEyaovLy814cUSUpISNCECRNUVVUVCCqtra2qqqrSI488Ym5xFuT3+7VkyRK98cYbev/99wkpV3HHHXfo448/Dtq3aNEiZWRk6PHHHyekXCYnJ+eKqe6ffvqpRo4caVJF1nb+/PkrfofHx8ertbU1rPMRVAwwYsSIoO0BAwZIkr7xjW9o2LBhZpRkaV988YVuv/12jRw5Us8++6y+/PLLwDGXy2ViZeYrKirSwoULNXHiRE2ePFkbN27UuXPntGjRIrNLs5zCwkJt27ZNb731lgYOHCi32y1Jcjqd6tevn8nVWcvAgQOvGLtz3XXXafDgwYzp6cCyZct06623as2aNbr//vu1f/9+vfTSS3rppZfMLs2S5syZo6efflojRozQt771Lf3ud7/T+vXr9eCDD4Z3wmudhoSuHT16lOnJV1FeXu6X1OEDfv/zzz/vHzFihD8hIcE/efJk/759+8wuyZI6+x4qLy83u7SowPTkq9u5c6c/KyvLb7fb/RkZGf6XXnrJ7JIsy+v1+h999FH/iBEj/ImJif4bbrjB/4//+I9+n88X1vkYowIAACyLgQAAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCyCCoAAMCy/n+djjKcH6FahwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.errorbar(data_x, data_y, sigma_y, sigma_x, fmt=\"o\");" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:25:44.032210Z", "start_time": "2020-02-21T10:25:43.648365Z" } }, "outputs": [ { "data": { "text/plain": [ "Array(876.49545695, dtype=float64)" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# define the cost function\n", "@jax.jit\n", "def cost(par):\n", " result = 0.0\n", " for xi, yi in zip(data_x, data_y):\n", " y_var = sigma_y ** 2 + (f_prime(xi, par) * sigma_x) ** 2\n", " result += (yi - f(xi, par)) ** 2 / y_var\n", " return result\n", "\n", "cost.errordef = Minuit.LEAST_SQUARES\n", "\n", "# test the jit-ed function\n", "cost(np.zeros(3))" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:25:44.059729Z", "start_time": "2020-02-21T10:25:44.034029Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 23.14 Nfcn = 91
EDM = 3.12e-05 (Goal: 0.0002)
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance accurate
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 1.25 0.15
1 x1 1.5 0.5
2 x2 1.6 1.5
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1 x2
x0 0.0223 -0.039 (-0.530) -0.150 (-0.657)
x1 -0.039 (-0.530) 0.24 0.17 (0.230)
x2 -0.150 (-0.657) 0.17 (0.230) 2.32
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 23.14 │ Nfcn = 91 │\n", "│ EDM = 3.12e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ Below EDM threshold (goal x 10) │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ No parameters at limit │ Below call limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Hesse ok │ Covariance accurate │\n", "└──────────────────────────────────┴──────────────────────────────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 1.25 │ 0.15 │ │ │ │ │ │\n", "│ 1 │ x1 │ 1.5 │ 0.5 │ │ │ │ │ │\n", "│ 2 │ x2 │ 1.6 │ 1.5 │ │ │ │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬──────────────────────┐\n", "│ │ x0 x1 x2 │\n", "├────┼──────────────────────┤\n", "│ x0 │ 0.0223 -0.039 -0.150 │\n", "│ x1 │ -0.039 0.24 0.17 │\n", "│ x2 │ -0.150 0.17 2.32 │\n", "└────┴──────────────────────┘" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = Minuit(cost, np.zeros(3))\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "ExecuteTime": { "end_time": "2020-02-21T10:25:44.566228Z", "start_time": "2020-02-21T10:25:44.065443Z" } }, "outputs": [ { "data": { "image/png": "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IYFdu3bx2WefOZ045VcPpKWlOXVeYQ4ePIjVauXBBx8kPj7e/lq/fj27du0iPj6eiRMn2o8v7Gfj6md69913qVq1Ktdff32xx7nrvrdo0YJdu3bZe67kW79+vX1/UVJTU8nLyyuwPScnB7BNG5Avf6TeCxOd/OSuNFVbIu6ikg+pFN544w3A9o3QmW/z5/v888/p37+/fT0vL4/t27dz9dVXFzj2559/pmXLlvb17OxsbrrpJh599FHuueceZs2axZtvvsl9991HdnY2CQkJjB49mpUrV/Lxxx+TkJDA7t27+eKLL0psG+KOtgd5eXnccsstrF27liVLltCxY8cir3HkyBH7t+V8OTk5vPPOO4SGhjo84DMyMti/fz+1atUqsYfJ+Zo1a8Ynn3xSYPvjjz/OqVOneOWVV2jYsKF9+4U/G2c/U76jR4/yzTffcNtttxEWFlbsse5q83HzzTczbdo05syZw7///W/ANuLp3Llzad++PXXr1gUKv5eXXHIJX331Fbt27eKSSy6xX3PRokX4+flx5ZVX2rcNGjSIqVOn8tZbbzmUpL355psEBATYe86IeITZ9T4i5e333383wsLCDMBo166dS9f4448/DMBYtWqVfdv27dsNwHj33Xcdjs3IyDD8/f2NyZMn27etWrXKiIuLs69brVbj4osvNubPn2+sWbOmwKBlHTp0MBYuXGgYRunbhpTFQw89ZABGQkKCMX/+/AKv891www1Gz549jQkTJhhvvPGGMWnSJKNJkyYGYLzwwgsOx+b3CElMTHTYPn36dGPSpEnG8OHDDcC46aabjEmTJhmTJk0yTp48WWSchbX5KOxn4+xnOj8uwEhKSirplrnVwIEDjYCAAOPhhx82Zs+ebXTq1MkICAhwaJ9R2L1cvXq14e/vb0RFRRkTJ040ZsyYYVx77bUGYNxzzz0F3ueuu+4yAGPQoEHGjBkzjIEDBxqAR9sUiRiGYSj5kArNarUa3bt3N2rUqGEMGzbMqFq1qlMNIvPld/nMycmxb3v//fcLHV1yw4YNBmB89tln9m2LFi0yunTp4nBchw4djPnz5xuLFy8usO+WW24xpk2bZhiGZ5KP7t27F9tV9HyLFi0yevfubURHRxsBAQFGjRo1jN69extLliwpcN2iko/69eu71C21sOSjsJ+Ns58pX4cOHYyoqCgjNze3mLvl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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.errorbar(data_x, data_y, sigma_y, sigma_x, fmt=\"o\", label=\"data\")\n", "x = np.linspace(data_x[0], data_x[-1], 200)\n", "par = np.array(m.values)\n", "plt.plot(x, f(x, par), label=\"fit\")\n", "plt.legend()\n", "\n", "# check fit quality\n", "chi2 = m.fval\n", "ndof = len(data_y) - 3\n", "plt.title(f\"$\\\\chi^2 / n_\\\\mathrm{{dof}} = {chi2:.2f} / {ndof} = {chi2/ndof:.2f}$\");" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We obtained a good fit." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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": "python3", "version": "3.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)]" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/basic.ipynb0000644000000000000000000022147514332717401015114 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Basics\n", "\n", "You will learn basic usage of iminuit and how to approach standard fitting problems with `iminuit`.\n", "\n", "`iminuit` is a Python frontend to the `Minuit2` library in C++, an integrated software that combines a local minimizer (called MIGRAD) and two error calculators (called HESSE and MINOS). You provide it an analytical function, which accepts one or several parameters, and an initial guess of the parameter values. It will then find a local minimum of this function starting from the initial guess. In that regard, iminuit minimizer is like other local minimizers, like those in `scipy.optimize`.\n", "\n", "In addition, iminuit has the ability to compute **uncertainty estimates** for model parameters. `iminuit` was designed to solve statistics problems, where uncertainty estimates are an essential part of the result. The two ways of computing uncertainty estimates, HESSE and MINOS, have different advantages and disadvantages.\n", "\n", "`iminuit` is the successor of `pyminuit`. If you used `pyminuit` before, you will find iminuit very familiar. An important feature of `iminuit` (and `pyminuit`) is that it uses introspection to detect the parameter names of your function. This is very convenient, especially when you work interactively in a Jupyter notebook. It also provides special output routines for Jupyter notebooks to pretty print the fit results, as you will see below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# basic setup of the notebook\n", "from matplotlib import pyplot as plt\n", "import numpy as np\n", "\n", "# everything in iminuit is done through the Minuit object, so we import it\n", "from iminuit import Minuit\n", "\n", "# we also need a cost function to fit and import the LeastSquares function\n", "from iminuit.cost import LeastSquares\n", "\n", "# display iminuit version\n", "import iminuit\n", "print(\"iminuit version:\", iminuit.__version__)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Quick start\n", "\n", "In this first section, we look at a simple case where line should be fitted to scattered $(x, y)$ data. A line has two parameters $(\\alpha, \\beta)$. We go through the full fit, showing all basic steps to get you started quickly. In the following sections we will revisit the steps in more detail." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# our line model, unicode parameter names are supported :) \n", "def line(x, α, β):\n", " return α + x * β\n", "\n", "\n", "# generate random toy data with random offsets in y\n", "rng = np.random.default_rng(1)\n", "data_x = np.linspace(0, 1, 10)\n", "data_yerr = 0.1 # could also be an array\n", "data_y = rng.normal(line(data_x, 1, 2), data_yerr)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To recover the parameters α and β of the line model from this data, we need to minimize a suitable cost function. The cost function must be twice differentiable and have a minimum at the optimal parameters. We use the method of least-squares here, whose cost function computes the sum of squared residuals between the model and the data. The task of iminuit is to find the minimum of that function.\n", "\n", "The iminuit module provides the `LeastSquares` class to conveniently generate a least-squares cost function.\n", "We will revisit how to write one by hand in a later section. Using a built-in cost function comes with some perks, for example, the fit (if data are 1D) is automatically visualized in a Jupyter notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "least_squares = LeastSquares(data_x, data_y, data_yerr, line)\n", "\n", "m = Minuit(least_squares, α=0, β=0) # starting values for α and β\n", "\n", "m.migrad() # finds minimum of least_squares function\n", "m.hesse() # accurately computes uncertainties" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "And that is all for a basic fit. The fit result is immediately visible here in the notebook, since calls to `m.migrad()` and `m.hesse()` return the `Minuit` object, which then automatically renders its state in a Jupyter notebook.\n", "\n", "The automatically generated plot of the fitted function is intentionally very basic. You can make a nicer plot by hand with `matplotlib`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw data and fitted line\n", "plt.errorbar(data_x, data_y, data_yerr, fmt=\"ok\", label=\"data\")\n", "plt.plot(data_x, line(data_x, *m.values), label=\"fit\")\n", "\n", "# display legend with some fit info\n", "fit_info = [\n", " f\"$\\\\chi^2$/$n_\\\\mathrm{{dof}}$ = {m.fval:.1f} / {m.ndof:.0f} = {m.fmin.reduced_chi2:.1f}\", \n", "]\n", "for p, v, e in zip(m.parameters, m.values, m.errors):\n", " fit_info.append(f\"{p} = ${v:.3f} \\\\pm {e:.3f}$\")\n", "\n", "plt.legend(title=\"\\n\".join(fit_info), frameon=False)\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"y\");" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "In the following, we dive into details step by step; how the Minuit object is initialized, how to run the algorithms, and how to get the results.\n", "\n", "`iminuit` was designed to make it easy to fit cost functions like `least_squares(...)`, where the parameters are individual arguments of the function. There is an alternative function signature that Minuit supports, which is more convenient when you explore models that have a not-yet-defined number of parameters, for example, a polynomial. Here, the parameters are passed as a NumPy array. We will discuss both in the following, but focus on the first.\n", "\n", "## Initialize the Minuit object\n", "\n", "To minimize a function, one has to create an instance of the Minuit class and pass the function and a starting value for each parameter. This does not start the minimization yet, this will come later.\n", "\n", "The `Minuit` object uses introspection to get the number and names of the function parameters automatically, so that they can be initialized with keywords." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(least_squares, α=0, β=0)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "If we forget a parameter or mistype them, Minuit will raise an error. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " Minuit(least_squares)\n", "except:\n", " import traceback\n", " traceback.print_exc()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " Minuit(least_squares, a=0, b=0)\n", "except:\n", " import traceback\n", " traceback.print_exc()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Initial parameter values\n", "\n", "The main algorithm MIGRAD is a local minimizer. It searches for a local minimum by a doing a mix of Newton steps and gradient-descents from a starting point. If your function has several minima, the minimum found will depend on the starting point. Even if it has only one minimum, iminuit will converge to it faster if you start in the proximity of the minimum.\n", "\n", "You can set the starting point using the parameter names as keywords, ` = `." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(least_squares, α=5, β=5) # pass starting values for α and β" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, the starting values can also be passed as positional arguments." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(least_squares, 5, 5) # another way of passing starting values for α and β" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "You can also use iminuit with functions that accept NumPy arrays. This has pros and cons.\n", "\n", "**Pros**\n", "\n", "- Easy to change number of fitted parameters\n", "- Sometimes simpler function body that's easier to read\n", "- Technically this is more efficient, but this is hardly going to be noticeable\n", "\n", "**Cons**\n", "\n", "- iminuit cannot figure out names for each parameter\n", "\n", "To demonstrate, use a version of the line model which accepts the parameters as a NumPy array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def line_np(x, par):\n", " return np.polyval(par, x) # for len(par) == 2, this is a line" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Calling `line_np` with more or less arguments is easy and will use a polynomial of the corresponding order to predict the behavior of the data.\n", "\n", "The built-in cost functions support such a model. For it to be detected properly, you need to pass the starting values in form a single sequence of numbers. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "least_squares_np = LeastSquares(data_x, data_y, data_yerr, line_np)\n", "\n", "Minuit(least_squares_np, (5, 5)) # pass starting values as a sequence" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Any sequence will work for initialization, you can also pass a list or a NumPy array here. `iminuit` uses the length of the sequence to detect how many parameters the model has. By default, the parameters are named automatically `x0` to `xN`. One can override this with the keyword `name`, passing a sequence of parameter names." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(least_squares_np, (5, 5), name=(\"a\", \"b\"))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Since `least_squares_np` works for parameter arrays of any length, one can easily change the number of fitted parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# fit a forth order polynomial\n", "Minuit(least_squares_np, (5, 5, 5, 5))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "It is often useful to try different orders of a polynomial model. If the order is too small, the polynomial will not follow the data. If it is too large, it will overfit the data and pick up random fluctuations and not the underlying trend. One can figure out the right order by experimenting or using an algorithm like cross-validation.\n", "\n", "### Inspecting current parameters\n", "\n", "You can check the current parameter values and settings with the method `Minuit.params` at any time. It returns a special list of `Param` objects which pretty-prints in Jupyter and in the terminal." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This produces a nice table with numbers rounded according to the rules of the *Particle Data Group*. The table will be updated once you run the actual minimization. To look at the initial conditions later, use `Minuit.init_params`. We will come back to the meaning of *Hesse Error* and *Minos Error* later.\n", "\n", "`Minuit.params` returns a tuple-like container of `Param` objects, which are data objects with attributes that one can query. Use `repr()` to get a detailed representation of the data object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p in m.params:\n", " print(repr(p), \"\\n\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Parameters with limits\n", "\n", "`iminuit` allows you to set parameter limits. Often a parameter is limited mathematically or physically to a certain range. For example, if your function contains `sqrt(x)`, then $x$ must be non-negative, $x \\ge 0$. You can set upper-, lower-, or two-sided limits for each parameter individually with the `limits` property.\n", "\n", "- Lower limit: use `Minuit.limits[] = (, None)` or `(, float(\"infinity\"))`\n", "- Upper limit: use `Minuit.limits[] = (None, )` or `(-float(\"infinity\"), )`\n", "- Two-sided limit: use `Minuit.limits[] = (, )`\n", "- Remove limits: use `Minuit.limits[] = None` or `(-float(\"infinity\"), float(\"infinity\")`\n", "\n", "You can also set limits for several parameters at once with a sequence. To impose the limits $α \\ge 0$ and $0 \\le β \\le 10$ in our example, we use:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.limits = [(0, None), (0, 10)]\n", "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible for the cost function to declare limits on its parameters. For this you need the `Annotated` type, which is available in Python-3.9 or later, and from the package `typing-extensions` in Python-3.8. The restrictions should be imported from the external package `annotated-types`. The built-in cost functions propagate such annotations of model parameters. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Annotated and Gt are imported from iminuit.typing here for universal compatibility, \n", "# but users should in general import them from external packages `typing-extensions` and\n", "# `annotated-types` to decouple models from the `iminuit` package\n", "from iminuit.typing import Annotated, Gt\n", "\n", "def line_with_positive_slope(x, slope: Annotated[float, Gt(0)], offset):\n", " return slope * x + offset\n", "\n", "lsq = LeastSquares(data_x, data_y, data_yerr, line_with_positive_slope)\n", "\n", "Minuit(lsq, 1, 0)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "You can reset the limit automatically set by such an annotation by calling `minuit_instance.limit[\"slope\"] = None` before fitting, if you wish.\n", "\n", "### Fixing and releasing parameters\n", "\n", "Sometimes you have a parameter which you want to set to a fixed value temporarily. Perhaps you have a guess for its value, and you want to see how the other parameters adapt when this parameter is fixed to that value.\n", "\n", "Or you have a complex function with many parameters that do not all affect the function at the same scale. Then you can manually help the minimizer to find the minimum faster by first fixing the less important parameters to initial guesses and fit only the important parameters. Once the minimum is found under these conditions, you can release the fixed parameters and optimize all parameters together. Minuit remembers the last state of the minimization and starts from there. The minimization time roughly scales with the square of the number of parameters. Iterated minimization over subspaces of the parameters can reduce that time.\n", "\n", "To fix an individual parameter, use `minuit_instance.fixed[] = True`. In our example, we fix α:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.fixed[\"α\"] = True\n", "m.params" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# migrad will not vary α, only β\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Now we release α and fix β and minimize again, you can also use the parameter index instead of its name." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.fixed[0] = False\n", "m.fixed[1] = True\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We could iterate this and would slowly approach the minimum, but that's silly; instead we release both parameters and run again. The array-like views support broadcasting to enable this shortcut notation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.fixed = False\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to fix a parameter and set a value with one convenient call, using `Minuit.fixto`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.fixto(\"α\", 3)\n", "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Varying starting points for minimization\n", "\n", "It is sometimes useful to change the values of some fixed parameters by hand and fit the others or to restart the fit from another starting point. For example, if the cost function has several minima, changing the starting value can be used to find the other minimum." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def cost_function_with_two_minima(x):\n", " return x ** 4 - x ** 2 + 1\n", "\n", "x = np.linspace(-1.5, 1.5)\n", "plt.plot(x, cost_function_with_two_minima(x));" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# starting at -0.1 gives the left minimum\n", "m = Minuit(cost_function_with_two_minima, x=-0.1)\n", "m.migrad()\n", "print(\"starting value -0.1, minimum at\", m.values[\"x\"])\n", "\n", "# changing the starting value to 0.1 gives the right minimum\n", "m.values[\"x\"] = 0.1 # m.values[0] = 0.1 also works\n", "m.migrad()\n", "print(\"starting value +0.1, minimum at\", m.values[\"x\"])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Advanced: Simplex and Scan minimizers \n", "\n", "`iminuit` also offers two other minimizers which are less powerful than MIGRAD, but may be useful in special cases.\n", "\n", "#### SIMPLEX\n", "\n", "The Nelder-Mead method (aka SIMPLEX) is well described on [Wikipedia](https://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method). It is a gradient-free minimization method that usually converges more slowly, but may be more robust. For some problems it can help to start the minimization with SIMPLEX and then finish with MIGRAD. Since the default stopping criterion for SIMPLEX is much more lax than MIGRAD, either running MIGRAD after SIMPLEX or reducing the tolerance with `Minuit.tol` is strongly recommended." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(cost_function_with_two_minima, x=10).simplex()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's run MIGRAD after SIMPLEX to finish the minimization." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(cost_function_with_two_minima, x=10).simplex().migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This combination uses slightly fewer function evaluations and produced a more accurate result than just running MIGRAD alone in this case (for another problem this may not be true)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(cost_function_with_two_minima, x=10).migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Scan\n", "\n", "Scan is a last resort. It does an N-dimensional grid scan over the parameter space. The number of function evaluations scale like $n^k$, where $k$ is the number of parameters and $n$ the number of steps along one dimension. Using scan for high-dimensional problems is unfeasible, but it can be useful in low-dimensional problems and when all but a few parameters are fixed. The scan needs bounds, which are best set with `Minuit.limits`. The number of scan points is set with the `ncall` keyword." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(cost_function_with_two_minima, x=10)\n", "m.limits = (-10, 10)\n", "m.scan(ncall=50)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The scan brought us in proximity of the minimum.\n", "\n", "In this case, the minimum is considered valid, because the EDM value is smaller than the EDM goal, but the scan may also end up in an invalid minimum, which is also ok. The scan minimizes the cost function using a finite number of steps, regardless of the EDM value (which is only computed after the scan for the minimum).\n", "\n", "One should always run MIGRAD or SIMPLEX after a SCAN." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Advanced: Errordef\n", "\n", "If you do not use one of the cost functions from the `iminuit.cost` module, you may need to pass an additional parameter to Minuit.\n", "\n", "Minuit by default assumes that the function scales like a chi-square function when one of the parameters is moved away from the minimum. If your cost function is constructed as a log-likelihood, it scales differently, and you must indicate that to Minuit wit the `errordef` parameter. Setting this is not needed for the cost functions in `iminuit.cost`.\n", "\n", "The `errordef` parameter is required to compute correct uncertainties. If you don't care about uncertainty estimates (but why are you using Minuit then?), you can ignore it. Minuit supports two kinds of cost functions, the *negative log-likelihood* and the *least-squares* function. Each has a corresponding value for `errordef`:\n", "\n", " - `0.5` or the constant `Minuit.LIKELIHOOD` for negative log-likelihood functions \n", " - `1` or the constant `Minuit.LEAST_SQUARES` for least-squares functions (the default)\n", "\n", "If you like to understand the origin of these numbers, have a look into the study **Hesse and Minos**, which explains in depth how uncertainties are computed.\n", "\n", "For our custom cost function, we could set `m.errordef=1` or `m.errordef=Minuit.LEAST_SQUARES`, which is more readable. An even better way is to add an attribute called `errordef` to the cost function. If such an attribute is present, Minuit uses it. Since this cost function has the default scaling, we do not need to set anything, but keep it in mind for negative log-likelihoods." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# a simple least-squares cost function looks like this...\n", "def custom_least_squares(a, b):\n", " ym = line(data_x, a, b)\n", " z = (data_y - ym) / data_yerr\n", " return np.sum(z ** 2)\n", "\n", "m = Minuit(custom_least_squares, 1, 2)\n", "m.migrad() # standard errordef, correct in this case" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.errordef = Minuit.LIKELIHOOD # errordef for negative log-likelihoods, wrong here\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The reported errors are now by a factor `sqrt(2)` smaller than they really are.\n", "\n", "### Advanced: Initial step sizes\n", "\n", "Minuit uses a gradient-descent method to find the minimum, and the gradient is computed numerically using finite differences. The initial step size is used to compute the first gradient. A good step size is small compared to the curvature of the function, but large compared to numerical resolution. Using a good step size can slightly accelerate the convergence, but Minuit is not very sensitive to the choice. If you don't provide a value, iminuit will guess a step size based on a heuristic.\n", "\n", "You can set initial step sizes with the `errors` property, `Minuit.errors[] = `. Using an appropriate step size is important when you have you a parameter which has physical bounds. Varying the initial parameter value by the step size may not create a situation where the parameter goes outside its bounds. For example, a parameter $x$ with $x > 0$ and initial value $0.1$ may not have a step size of $0.2$.\n", "\n", "In our example, we could use an initial step size of $\\Delta α = 0.1$ and $\\Delta β = 0.2$. Setting both can be done conveniently by assigning a sequence:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(least_squares, α=5, β=5)\n", "m.errors = (0.1, 0.2) # assigning sequences works\n", "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Broadcasting is also supported." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.errors = 0.3 # broadcasting\n", "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Only positive step sizes are allowed. Non-positive values are replaced with the heuristic and a warning is emitted." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.errors[\"β\"] = -0.3\n", "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Advanced: Override parameter name detection\n", "\n", "`iminuit` tries hard to detect the parameter names correctly. It works for a large variety of cases. For example, if you pass a functor instead of a function, it will use the arguments of the `__call__` method, automatically skipping `self`. It even tries to parse the docstring if all else fails.\n", "\n", "You can check which parameter names iminuit finds for your function with the `describe` function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from iminuit import describe\n", "\n", "\n", "def foo(x, y, z):\n", " pass\n", "\n", "\n", "assert describe(foo) == [\"x\", \"y\", \"z\"]\n", "\n", "\n", "class Foo:\n", " def __call__(self, a, b):\n", " pass\n", "\n", "\n", "assert describe(Foo()) == [\"a\", \"b\"]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes parameter names cannot be determined, for example, when a function accepts a variable number of arguments." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def func_varargs(*args): # function with variable number of arguments\n", " return np.sum((np.array(args) - 1) ** 2)\n", "\n", "assert describe(func_varargs) == []" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`describe` cannot detect the number and names of the parameters in this case and returns an empty list. If you work with functions that accept a variable number of arguments a lot, it is better to use a cost function which accepts a parameter array (this is explained in the next section).\n", "\n", "When iminuit cannot detect the arguments, but you know how many arguments there are, or if you simply want to override the names found by `iminuit`, you can do that with the keyword `name`, like so:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Minuit(func_varargs, name=(\"a\", \"b\"), a=1, b=2).migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Alternative interface: `iminuit.minimize`\n", "\n", "Those familiar with `scipy` may find the `minimize` function useful. It exactly mimics the function interface of `scipy.optimize.minimize`, but uses `Minuit` for the actual minimization. The `scipy` package must be installed to use it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from iminuit import minimize # has same interface as scipy.optimize.minimize\n", "\n", "minimize(least_squares_np, (5, 5))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This interface is handy if you want to be able to switch between iminuit and `scipy.optimize.minimize`, but we recommend the standard interface instead. It is an advantage of Minuit that you can interact and manually steer the minimization process. This is not as convenient with a functional interface like `minimize`." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Investigating the fit status\n", "\n", "Calling `Minuit.migrad()` runs the actual minimization with the MIGRAD algorithm. MIGRAD essentially tries a Newton-step and if that does not produce a smaller function value, it tries a line search along the direction of the gradient. So far so ordinary. The clever bits in MIGRAD are how various pathological cases are handled.\n", "\n", "Let's look again at the output of `Minuit.migrad()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(least_squares, α=5, β=5)\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The `Minuit.migrad` method returns the Minuit instance so that one can chain method calls. The instance also pretty prints the latest state of the minimization.\n", "\n", "The first block in this output is showing information about the function minimum. This is good for a quick check:\n", "\n", "- All blocks should be green.\n", "- Purple means something bad. \n", "- Yellow may be bad or not. Be careful.\n", "\n", "Let's see how it looks when the function is bad." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m_bad = Minuit(lambda x: 0, x=1) # a constant function has no minimum\n", "m_bad.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Coming back to our previous good example, the info about the function minimum can be directly accessed with `Minuit.fmin`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.fmin" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print(repr(...)) to see a detailed representation of the data object\n", "print(repr(m.fmin))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The most important one here is `is_valid`. If this is false, the fit did not converge and the result is useless. Since this is so often queried, a shortcut is provided with `Minuit.valid`.\n", "\n", "If the fit fails, there is usually a numerical or logical issue.\n", "\n", "- The fit function is not analytical everywhere in the parameter space or does not have a local minimum (the minimum may be at infinity, the extremum may be a saddle point or maximum). Indicators for this are `is_above_max_edm=True`, `hesse_failed=True`, `has_posdef_covar=False`, or `has_made_posdef_covar=True`. A non-analytical function is one with a discrete step, for example.\n", "- MIGRAD reached the call limit before the convergence so that `has_reached_call_limit=True`. The number of function calls is given by `nfcn`, and the call limit can be changed with the keyword argument `ncall` in the method `Minuit.migrad`. Note that `nfcn` can be slightly larger than `ncall`, because MIGRAD internally only checks this condition after a full iteration, in which several function calls can happen.\n", "\n", "MIGRAD detects convergence by a small `edm` value, the *estimated distance to minimum*. This is the difference between the current minimum value of the minimized function and the prediction based on the current local quadratic approximation of the function (something that MIGRAD computes as part of its algorithm). If the fit did not converge, `is_above_max_edm` is true.\n", "\n", "If you are interested in parameter uncertainties, you should make sure that:\n", "\n", "- `has_covariance`, `has_accurate_covar`, and `has_posdef_covar` are true.\n", "- `has_made_posdef_covar` and `hesse_failed` are false.\n", "\n", "The second object of interest after the fit is the parameter list, which can be directly accessed with `Minuit.params`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.params" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p in m.params:\n", " print(repr(p))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`m.params` is a tuple-like container of `Param` data objects which contain information about the fitted parameters. Important fields are:\n", "\n", "- `number`: parameter index.\n", "- `name`: parameter name.\n", "- `value`: value of the parameter at the minimum.\n", "- `error`: uncertainty estimate for the parameter value.\n", "\n", "Whether the uncertainty estimate is accurate depends on the correct mathematical modeling of your fitting problem and using the right `errordef` value for Minuit. What do we mean by correct mathematical modelling? If you look into the function `simple_least_squares(a, b)`, you see that each squared residual is divided by the expected variance of the residual. This is necessary to get accurate uncertainty estimates for the parameters.\n", "\n", "Sometimes the expected variance of the residual is not well known. If the cost function to minimize satisfies certain conditions, there is a simple test to check whether the residual variances are ok. One should look at the function value at the minimum, given by `Minuit.fmin.fval`, and divide it by the so-called degrees of freedom, which is difference of the number of residuals and the number of fitted parameters, and can be queried with the attribute `Minuit.ndof`. This is called reduced chi2, it can be directly queried with `Minuit.fmin.reduced_chi2`.\n", "\n", "The reduced chi2 is available for all built-in binned cost functions and the `LeastSquares` cost function in `iminuit.cost`. It cannot be automatically provided for unbinned cost functions, since that requires binning the data, which has to be defined by the user. For unbinned cost functions, you can still compute a reduced chi2 yourself, but it is not possible to do automatically. Querying `Minuit.fmin.reduced_chi2` is safe, it either returns a valid value or `nan` if the chi2 cannot be computed automatically for the current cost function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f\"𝜒²/ndof = {m.fval:.2f} / {m.ndof} = {m.fmin.reduced_chi2:.2f}\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This value should be around 1. The more data points one has, the closer. If the value is much larger than 1, then the data variance is underestimated or the model does not describe the data. If the value is much smaller than 1, then the data variance is overestimated (perhaps because of positive correlations between the fluctuations of the data values).\n", "\n", "The last block shows the covariance matrix, this is useful to check for large correlations which are usually a sign of trouble." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.covariance" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We will discuss this matrix in more detail in the next section.\n", "\n", "## Parameter uncertainties, covariance, and confidence intervals\n", "\n", "You saw how to get the uncertainty of each individual parameter and how to access the full covariance matrix of all parameters together, which includes the correlations. Correlations are essential additional information if you want to work with parameter uncertainties seriously.\n", "\n", "Minuit offers two ways to compute the parameter uncertainties, Hesse and Minos. Both have pros and cons.\n", "\n", "### Hesse for covariance and correlation matrices\n", "\n", "The Hesse algorithm numerically computes the matrix of second derivatives at the function minimum (called the Hesse matrix) and inverts it. The Hesse matrix is symmetric by construction. In the limit of infinite data samples to fit, the result of this computation converges to the true covariance matrix of the parameters. It is often already a good approximation even for finite statistic. These errors obtained from this method are sometimes called *parabolic errors*, because the Hesse matrix method is exact if the function is a hyperparabola (third and higher-order derivatives are all zero).\n", "\n", "**Pros**\n", "\n", "- (Comparably) fast computation.\n", "- Provides covariance matrix for error propagation.\n", "\n", "**Cons**\n", "\n", "- May not have good coverage probability when sample size is small\n", "\n", "The MIGRAD algorithm computes an approximation of the Hesse matrix automatically during minimization. When the default strategy is used, Minuit does a check whether this approximation is sufficiently accurate and if not, it computes the Hesse matrix automatically.\n", "\n", "All this happens inside C++ Minuit2 and is a bit intransparent, so to be on the safe side, we recommend to call `Minuit.hesse` explicitly after the minimization, if exact errors are important." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# let's mess up the current errors a bit so that hesse has something to do\n", "m.errors = (0.16, 0.2)\n", "m.params" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.hesse().params # note the change in \"Hesse Error\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Covariance and correlation Matrix\n", "\n", "To see the covariance matrix of the parameters, you do:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "inputHidden": false, "outputHidden": false }, "outputs": [], "source": [ "m.covariance" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The parameters α and β are strongly anti-correlated, the numerical value of the correlation is shown in parentheses. The correlation is also highlighted by the blue color of the off-diagonal elements." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(repr(m.covariance)) # use print(repr(...) to skip pretty printing" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To get the correlation matrix, use:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.covariance.correlation() # returns a newly created correlation matrix" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Nonzero correlation is not necessarily a bad thing, but if you have freedom in redefining the parameters of the fit function, it is good to chose parameters which are not strongly correlated.\n", "\n", "Minuit cannot accurately minimize the function if two parameters are (almost) perfectly (anti-)correlated. It also means that one of two parameters is superfluous, it doesn't add new information. You should rethink the fit function in this case and try to remove one of the parameters from the fit.\n", "\n", "Both matrices are subclasses of `numpy.ndarray`, so you can use them everywhere you would use a NumPy array. In addition, these matrices support value access via parameter names:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.covariance[\"α\", \"β\"]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### MINOS for non-parabolic minima\n", "\n", "Minuit has another algorithm to compute uncertainties: MINOS. It implements the so-called profile likelihood method, where the neighborhood around the function minimum is scanned until the contour is found where the function increase by the value of `errordef`. The contour defines a confidence region that covers the true parameter point with a certain probability. The probability is exactly known in the limit of infinitely large data samples, but approximate for the finite case. Please consult a textbook about statistics about the mathematical details or look at the tutorial \"Error computation with HESSE and MINOS\".\n", "\n", "**Pros**\n", "\n", "- Produces pretty confidence regions in 2D (or higher) for scientific plots\n", "\n", "**Cons**\n", "\n", "- Computationally expensive\n", "- Asymmetric errors are difficult to error-propagate\n", "\n", "MINOS is not automatically called during minimization, it needs to be called explicitly afterwards, like so:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.minos()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "By now you are probably used to see green colors, which indicate that Minos ran successful. Be careful when these are red instead, Minos can fail. The fields in the new Minos table mean the following:\n", "\n", "- Valid: Whether Minos considers the scan result valid.\n", "- At Limit: True if Minos hit a parameter limit before the finishing the contour, which would be bad.\n", "- Max FCN: True if Minos reached the maximum number of allowed calls before finishing the contour, also bad.\n", "- New Min: True if Minos discovered a deeper local minimum in the neighborhood of the current one. Not necessarily bad, but should not happen.\n", "\n", "The errors computed by Minos are now also shown in the parameter list." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.params" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "If the absolute values of the Minos errors are very close to the Hesse Error, the function is well approximated by a hyperparabola around the minimum. You can use this as a check instead of explicitly plotting the function around the minimum (for which we provide tools, see below).\n", "\n", "### Coverage probability of intervals constructed with Hesse and Minos algorithms\n", "\n", "In applications, it is important to construct confidence regions with a well-known coverage probability. As previously mentioned, the coverage probability of the intervals constructed from the uncertainties reported by Hesse and Minos are not necessarily the standard 68 %.\n", "\n", "Whether Hesse or Minos produce an interval with a coverage probability closer to the desired 68 % in finite samples depends on the case. There are theoretical results which suggest that Hesse may be slightly better, but we also found special cases where Minos intervals performed better.\n", "\n", "Some sources claim that Minos gives better coverage when the cost function is not parabolic around the minimum; that is not generally true, in fact Hesse intervals may have better coverage.\n", "\n", "As a rule-of-thumb, use Hesse as the default and try both algorithms if accurate coverage probability matters.\n", "\n", "## Quick access to fit results\n", "\n", "You get the main fit results with properties and methods from the `Minuit` object. We used several of them already. Here is a summary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(m.values) # array-like view of the parameter values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# access values by name or index\n", "print(\"by name \", m.values[\"α\"])\n", "print(\"by index\", m.values[0])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# iterate over values\n", "for key, value in zip(m.parameters, m.values):\n", " print(f\"{key} = {value}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# slicing works\n", "print(m.values[:1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(m.errors) # array-like view of symmetric uncertainties" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`Minuit.errors` supports the same access as `Minuit.values`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(m.params) # parameter info (using str(m.params))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(repr(m.params)) # parameter info (using repr(m.params))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# asymmetric uncertainties (using str(m.merrors))\n", "print(m.merrors)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# asymmetric uncertainties (using repr(m.merrors))\n", "print(repr(m.merrors))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(m.covariance) # covariance matrix computed by Hesse (using str(m.covariance))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(repr(m.covariance)) # covariance matrix computed by Hesse (using repr(m.covariance))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "As already mentioned, you can play around with iminuit by assigning new values to `m.values` and `m.errors` and then run `m.migrad()` again. The values will be used as a starting point.\n", "\n", "## Plotting\n", "\n", "`iminuit` comes with built-in methods to draw the likelihood around the minimum. These can be used to draw confidence regions with a defined confidence level or for debugging the likelihood.\n", "\n", "### Drawing confidence regions\n", "\n", "To get a generic overview, use the method `Minuit.draw_mnmatrix`. It shows scans over the likelihood where all other parameters than the ones scanned are minimized, in other words, it is using the Minos algorithm. The regions and intervals found in this way correspond to uncertainty intervals. It is also a great way to see whether the likelihood is sane around the minimum. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# find the minimum again after messing around with the parameters\n", "m.migrad()\n", "\n", "# draw matrix of likelihood contours for all pairs of parameters at 1, 2, 3 sigma\n", "m.draw_mnmatrix();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The diagonal cells show the 1D profile around each parameter. The points were the horizontal lines cross the profile correspond to confidence intervals with confidence level `cl` (a probability). The off-diagonal cells show confidence regions with confidence level `cl`. Asymptotically (in large samples), the `cl` is equal to the probability that the region contains the true value. In finite samples, this is usually only approximately so.\n", "\n", "For convenience, the drawing functions interpret `cl >= 1` as the number of standard deviations with a confidence level that corresponds to a standard normal distribution:\n", "\n", "- cl = 1: 68.3 %\n", "- cl = 2: 95.4 %\n", "- cl = 3: 99.7 %\n", "\n", "Drawing all profiles and regions can be time-consuming. The following commands show how to draw only individual contours or profiles." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw three confidence regions with 68%, 90%, 99% confidence level\n", "m.draw_mncontour(\"α\", \"β\", cl=(0.68, 0.9, 0.99));" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw three confidence regions with 68%, 90%, 99% confidence level\n", "m.draw_mncontour(\"α\", \"β\", cl=(0.68, 0.9, 0.99));" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get individual contours to plot them yourself\n", "pts = m.mncontour(\"α\", \"β\", cl=0.68, size=20)\n", "x, y = np.transpose(pts)\n", "plt.plot(x, y, \"o-\");" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To make the contour look nicer, you can increase the `size` parameter or use the `interpolated` parameter to do cubic spline interpolation or use the `experimental` algorithm." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw original points\n", "plt.plot(x, y, \".\", label=\"size=20\")\n", "\n", "# draw interpolated points\n", "pts2 = m.mncontour(\"α\", \"β\", cl=0.68, size=20, interpolated=100)\n", "x2, y2 = np.transpose(pts2)\n", "plt.plot(x2, y2, label=\"size=20, interpolated\")\n", "\n", "# actual curve at higher resolution\n", "pts = m.mncontour(\"α\", \"β\", cl=0.68, size=100)\n", "x3, y3 = np.transpose(pts)\n", "plt.plot(x3, y3, \"-\", label=\"size=100\")\n", "\n", "plt.legend();" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# with experimental algorithm\n", "pts = m.mncontour(\"α\", \"β\", cl=0.68, size=50, experimental=True)\n", "x4, y4 = np.transpose(pts)\n", "plt.plot(x4, y4, \"-\", label=\"size=50 experimental\");" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The experimental algorithm takes more time but produces a smoother contour.\n", "\n", "To draw the 1D profile, call `Minuit.draw_mnprofile`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.draw_mnprofile(\"α\");" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# or use this to plot the result of the scan yourself\n", "a, fa, ok = m.mnprofile(\"α\")\n", "plt.plot(a, fa);" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Likelihood debugging\n", "\n", "`mnmatrix`, `mnprofile`, and `mncontour` do Minos scans. If you have trouble with Minos or with the minimization, you should check how the likelihood looks like where you are. The following functions perform no minimization, they just draw the likelihood function as it is at certain coordinates." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw 1D scan over likelihood, the minimum value is subtracted by default\n", "m.draw_profile(\"α\");" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# or draw it yourself, the minimum value is not subtracted here\n", "x, y = m.profile(\"α\")\n", "plt.plot(x, y);" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# draw 2D scan over likelihood\n", "m.draw_contour(\"α\", \"β\");" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# or use this to plot the result of the scan yourself\n", "x, y, z = m.contour(\"α\", \"β\", subtract_min=True)\n", "cs = plt.contour(x, y, z, (1, 2, 3, 4)) # these are not sigmas, just the contour values\n", "plt.clabel(cs);" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Interactive fit\n", "\n", "In Jupyter notebooks, it is possible to fit a model to data interactively, by calling `Minuit.interactive`. This functionality requires optional extra packages. If they are not there, you will get a friendly error message telling you what you need to install." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "You can change the parameter values with the sliders. Clicking the \"Fit\" button runs `Minuit.migrad` with these as starting values.\n", "\n", "**Note:** If you see this notebook on ReadTheDocs or otherwise statically rendered, changing the sliders won't change the plot. This requires a running Jupyter kernel.\n", "\n", "Interactive fits are useful to find starting values and to debug the fit. The following issues are easy to detect:\n", "\n", "- Starting values are way off.\n", "- You forgot to set limits on some parameters.\n", "- Some parameters are strongly correlated.\n", "- Your model is not analytical.\n", "\n", "Strong correlations are caused when a change to one parameter can be almost perfectly undone by a changing one or more other parameters. If the model suddenly jumps when you move the sliders, this may indicate that the model is not analytical, but also note that the sliders have finite resolution and the model curve is also only drawn with finite resolution. Set tighter limits on the affected parameter or investigate the root cause with numerical experiments.\n", "\n", "`Minuit.interactive` uses the `visualize` method on the cost function, if it is available. All built-in cost functions provide this method, but it only works for 1D distributions, since there is no obvious general way to visualize data-model agreement in higher dimensions. You can provide your visualization though, see the documentation of `Minuit.interactive`. This can also be useful to draw the model in more detail, for example, if you want to give different components in an additive model different colors (e.g. signal and background)." ] } ], "metadata": { "kernel_info": { "name": "python3" }, "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "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.9.16" }, "nteract": { "version": "0.12.3" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/binned_vs_unbinned.ipynb0000644000000000000000000146213614332717401017666 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Binned vs. unbinned fits\n", "\n", "We compare binned with unbinned fit applied to a toy example of a gaussian signal peak over exponential background.\n", "\n", "For one-dimensional data, binned fit are preferred. They are usually considerably faster than an unbinned fit and more numerically stable. For multi-dimensional data, however, an unbinned fit can be faster.\n", "\n", "It is a common misconception that binned fits are inherently biased. This idea originates from the past when it was common (at least in particle physics) to fit binned data with the least-squares method, which is indeed biased, see [Dembinski, Schmelling, Waldi, *Application of the Iterated Weighted Least-Squares Fit to counting experiments*, NIM A 940 (2019) 135-141](https://doi.org/10.1016/j.nima.2019.05.086). That bias can be completely avoided, however, if the fit uses the maximum-likelihood method and a Poisson distribution to describe the observed bin contents as a function of the predicted ones, and if the model prediction for a bin content is properly computed by integrating over the model density, instead of computing it from the density at the bin center times the bin width. The cost functions `BinnedNLL` and `ExtendedBinnedNLL` from `iminuit.cost` use the correct calculation.\n", "\n", "So there is no need to worry bias, but some information is lost in the binning process - the densities of events inside each bin. This loss can be made negligible by making the bin width small enough. How small the bins have to be depends on the sensitivity of the model parameter on this particular loss of information. In this tutorial we demonstrate this and also demonstrate the difference in run-time of unbinned and binned fits.\n", "\n", "**Conclusions:** With only 20 bins, the binned fit reached an accuracy for the signal yield that is comparable to the unbinned fit. With 50 bins, also all shape parameters have uncertainties that are less than 5 % larger than those in the unbinned fit. At the same time, the binned fit is much faster. Even with 200 bins, the binned fit is two orders of magnitude faster than the unbinned fit. In practice, this is a huge difference, 3 seconds vs. 5 minutes.\n", "\n", "You can try to run this notebook with a data sample contains less points, then the difference will not be as dramatic." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from numba_stats import norm, expon\n", "import matplotlib.pyplot as plt\n", "from iminuit import Minuit\n", "from iminuit.cost import ExtendedUnbinnedNLL, ExtendedBinnedNLL\n", "import joblib\n", "from IPython.display import display" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# sample size, change this to see how the results of the comparison change\n", "n = 100_000\n", "truth = np.array((1.0, 1.0, 1.0, 0.1, 1.0))\n", "\n", "rng = np.random.default_rng(1)\n", "s = rng.normal(truth[2], truth[3], size=int(n * truth[0]))\n", "b = rng.exponential(truth[4], size=int(n * truth[1]))\n", "pts = np.append(s, b)\n", "pts = pts[(pts > 0) & (pts < 2)]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def fit(c):\n", " m = Minuit(c, s=1, b=1, mu=1, sigma=0.1, tau=1)\n", " m.limits[\"s\", \"b\", \"sigma\", \"tau\"] = (0, None)\n", " m.limits[\"mu\"] = (0, 2)\n", " m.migrad()\n", " assert m.valid\n", " return m" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = -4.08e+06 Nfcn = 120
EDM = 1.25e-05 (Goal: 0.0002) time = 0.3 sec
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.008 0.005 0
2 mu 999.3e-3 0.4e-3 0 2
3 sigma 99.21e-3 0.34e-3 0
4 tau 1.002 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.32e-05 -4.96e-06 (-0.281) -3.97e-08 (-0.029) 2.99e-07 (0.246) -3.97e-06 (-0.159)
b -4.96e-06 (-0.281) 2.36e-05 8.27e-10 -4.6e-07 (-0.283) 1.96e-05 (0.586)
mu -3.97e-08 (-0.029) 8.27e-10 1.46e-07 -5.62e-09 (-0.044) -1.42e-07 (-0.054)
sigma 2.99e-07 (0.246) -4.6e-07 (-0.283) -5.62e-09 (-0.044) 1.12e-07 -3.62e-07 (-0.157)
tau -3.97e-06 (-0.159) 1.96e-05 (0.586) -1.42e-07 (-0.054) -3.62e-07 (-0.157) 4.76e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = -4.08e+06 │ Nfcn = 120 │\n", "│ EDM = 1.25e-05 (Goal: 0.0002) │ time = 0.3 sec │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.008 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.3e-3 │ 0.4e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.21e-3 │ 0.34e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.32e-05 -4.96e-06 -3.97e-08 2.99e-07 -3.97e-06 │\n", "│ b │ -4.96e-06 2.36e-05 8.27e-10 -4.6e-07 1.96e-05 │\n", "│ mu │ -3.97e-08 8.27e-10 1.46e-07 -5.62e-09 -1.42e-07 │\n", "│ sigma │ 2.99e-07 -4.6e-07 -5.62e-09 1.12e-07 -3.62e-07 │\n", "│ tau │ -3.97e-06 1.96e-05 -1.42e-07 -3.62e-07 4.76e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def density(x, s, b, mu, sigma, tau):\n", " xrange = (0, 2)\n", " s1 = s * n * np.diff(norm.cdf(xrange, mu, sigma))\n", " b1 = b * n * np.diff(expon.cdf(xrange, 0, tau))\n", " return s1 + b1, (\n", " s * n * norm.pdf(x, mu, sigma) + \n", " b * n * expon.pdf(x, 0, tau)\n", " )\n", "\n", "m = fit(ExtendedUnbinnedNLL(pts, density))\n", "par_names = [m.params[i].name for i in range(m.npar)]\n", "results = {np.inf: (np.array(m.values), np.array(m.errors), m.fmin.time)}\n", "m" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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KCee/cSz37S2V9VjOtkYvP5g4w86HKOV//6pA9ExWlnH27aijCSrwt6gr7J+PULfeQn63FD3HQgBBRgREZEo4yUmeEp7UD2DUEvK7MUfaoK3HKOlFXiujFYcq9Hu8pwk+/pZDlGAERERaaa8xOChJR5aHg9AtQxC28eMCFtdtaHLjoqIiEjUUYARERGRqKMAIyIiIlFHAUZERESijgKMiIiIRJ2IDjDPPfccOTk5JCYmMmjQID7//HOnSxIREZEIELEB5h//+Af33nsvDz/8MCtWrODcc89l+PDhFBY2cLZBERERiXoRG2Ceeuop7rzzTm677TZ69erFtGnTaNGiBS+99JLTpYmIiIjDInIiu4qKCpYvX84DDzwQXOdyuRg6dChLliw56WvKy8spLy8PPi4uti+V7vF4Ql6fr7w05NsUERGJJuH4fq2+XWNOP6NeRAaYAwcO4PV6ycjIqLE+IyODL7/88qSvmTRpEo888sgJ67Ozs8NSo4iISHPmnhLe7R85cgS3233K5yMywNTHAw88wL333ht87PP5OHToEG3atMGyrJC9j8fjITs7m127dpGSkhKy7UaSpr6P2r/o19T3sanvHzT9fdT+1Z8xhiNHjpCVdfqrXEdkgGnbti0xMTEUFBTUWF9QUEBmZuZJX5OQkEBCQkKNdampqeEqkZSUlCb5j7K6pr6P2r/o19T3sanvHzT9fdT+1c/pel4CInIQb3x8PAMGDGDevHnBdT6fj3nz5jF48GAHKxMREZFIEJE9MAD33nsvt9xyCwMHDuSCCy5gypQplJSUcNtttzldmoiIiDgsYgPMTTfdxP79+5k4cSL5+fn069ePOXPmnDCwt7ElJCTw8MMPn3C4qilp6vuo/Yt+TX0fm/r+QdPfR+1f+FnmTOcpiYiIiESYiBwDIyIiInI6CjAiIiISdRRgREREJOoowIiIiEjUUYABnnvuOXJyckhMTGTQoEF8/vnnp23/5ptv0qNHDxITE+nbty/vvfdejeeNMUycOJH27duTlJTE0KFD2bRpUzh34bTqsn9/+ctfuPTSS2ndujWtW7dm6NChJ7S/9dZbsSyrxnL11VeHezdOqy77OH369BPqT0xMrNEmmj/DK6644oT9syyLESNGBNtE0me4aNEirrvuOrKysrAsi3feeeeMr1mwYAH9+/cnISGBbt26MX369BPa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fr4xxpibb77ZTJgwIdj+008/NbGxsebJJ580GzduNA8//LCJi4sza9euDbZ5/PHHTWpqqnn33XfNmjVrzPXXX29yc3PNsWPHIn7/Hn/8cRMfH2/++c9/mn379gWXI0eOGGOMOXLkiPnFL35hlixZYrZt22Y++ugj079/f3PWWWeZsrKyRt+/+uzjI488Yj744AOzZcsWs3z5cjNq1CiTmJho1q9fH2wTzZ9hwCWXXGJuuummE9ZH2md45MgRs3LlSrNy5UoDmKeeesqsXLnS7NixwxhjzIQJE8zNN98cbL9161bTokULc99995mNGzea5557zsTExJg5c+YE25zpZxbJ+zdz5kwTGxtrnnvuuRr/B4uKioJtfv7zn5sFCxaYbdu2mU8//dQMHTrUtG3b1hQWFjb6/hlT9318+umnzTvvvGM2bdpk1q5da372s58Zl8tlPvroo2CbaP4MA37wgx+YQYMGnXSbkfQZ3n333cbtdpsFCxbU+DdXWloabBOJ34XNPsAYY8yzzz5rOnXqZOLj480FF1xgPvvss+Bzl19+ubnllltqtH/jjTfM2WefbeLj403v3r3N7Nmzazzv8/nMQw89ZDIyMkxCQoIZMmSIycvLa4xdOam67F/nzp0NcMLy8MMPG2OMKS0tNcOGDTPt2rUzcXFxpnPnzubOO+905JdKdXXZx/HjxwfbZmRkmGuvvdasWLGixvai+TM0xpgvv/zSAObDDz88YVuR9hkGTqn9+hLYp1tuucVcfvnlJ7ymX79+Jj4+3nTp0sW8/PLLJ2z3dD+zxlTX/bv88stP294Y+7Tx9u3bm/j4eNOhQwdz0003mc2bNzfujlVT13184oknTNeuXU1iYqJJS0szV1xxhZk/f/4J243Wz9AY+5ThpKQk88ILL5x0m5H0GZ5s34Aa/68i8bvQ8hcvIiIiEjWa9RgYERERiU4KMCIiIhJ1FGBEREQk6ijAiIiISNRRgBEREZGoowAjIiIiUUcBRkRERKKOAoyIiIhEHQUYERERiToKMCIiIhJ1FGBEREQk6ijAiEhU2L9/P5mZmTz22GPBdYsXLyY+Pp558+Y5WJmIOEEXcxSRqPHee+8xcuRIFi9eTPfu3enXrx/XX389Tz31lNOliUgjU4ARkagyduxYPvroIwYOHMjatWtZtmwZCQkJTpclIo1MAUZEosqxY8fo06cPu3btYvny5fTt29fpkkTEARoDIyJRZcuWLezduxefz8f27dudLkdEHKIeGBGJGhUVFVxwwQX069eP7t27M2XKFNauXUt6errTpYlII1OAEZGocd999/HPf/6T1atX06pVKy6//HLcbjezZs1yujQRaWQ6hCQiUWHBggVMmTKFv//976SkpOByufj73//Of//7X55//nmnyxORRqYeGBEREYk66oERERGRqKMAIyIiIlFHAUZERESijgKMiIiIRB0FGBEREYk6CjAiIiISdRRgREREJOoowIiIiEjUUYARERGRqKMAIyIiIlFHAUZERESizv8DPHQmEjteJGQAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "xm = np.linspace(np.min(pts), np.max(pts), 1000)\n", "_, ym = density(xm, *m.values)\n", "plt.hist(pts, bins=100, range=(0, 2), label=\"data\")\n", "dx = 2 / 100\n", "plt.plot(xm, ym * dx, label=\"fit\")\n", "plt.legend()\n", "plt.xlabel(\"x\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This fit is unbinned, the observed sample is binned here only for visualisation." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 190.9 (chi2/ndof = 1.0) Nfcn = 110
EDM = 2.17e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.008 0.005 0
2 mu 999.3e-3 0.4e-3 0 2
3 sigma 99.22e-3 0.34e-3 0
4 tau 1.002 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.32e-05 -5.01e-06 (-0.284) -3.98e-08 (-0.029) 3.02e-07 (0.248) -4.04e-06 (-0.161)
b -5.01e-06 (-0.284) 2.37e-05 8.59e-10 -4.66e-07 (-0.286) 1.97e-05 (0.587)
mu -3.98e-08 (-0.029) 8.59e-10 1.46e-07 -5.93e-09 (-0.046) -1.43e-07 (-0.054)
sigma 3.02e-07 (0.248) -4.66e-07 (-0.286) -5.93e-09 (-0.046) 1.12e-07 -3.69e-07 (-0.159)
tau -4.04e-06 (-0.161) 1.97e-05 (0.587) -1.43e-07 (-0.054) -3.69e-07 (-0.159) 4.77e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 190.9 (chi2/ndof = 1.0) │ Nfcn = 110 │\n", "│ EDM = 2.17e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.008 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.3e-3 │ 0.4e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.22e-3 │ 0.34e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.32e-05 -5.01e-06 -3.98e-08 3.02e-07 -4.04e-06 │\n", "│ b │ -5.01e-06 2.37e-05 8.59e-10 -4.66e-07 1.97e-05 │\n", "│ mu │ -3.98e-08 8.59e-10 1.46e-07 -5.93e-09 -1.43e-07 │\n", "│ sigma │ 3.02e-07 -4.66e-07 -5.93e-09 1.12e-07 -3.69e-07 │\n", "│ tau │ -4.04e-06 1.97e-05 -1.43e-07 -3.69e-07 4.77e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 87.19 (chi2/ndof = 0.9) Nfcn = 110
EDM = 2.41e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.008 0.005 0
2 mu 999.3e-3 0.4e-3 0 2
3 sigma 99.24e-3 0.34e-3 0
4 tau 1.002 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.32e-05 -5.02e-06 (-0.284) -3.97e-08 (-0.029) 3.04e-07 (0.249) -4.05e-06 (-0.161)
b -5.02e-06 (-0.284) 2.37e-05 4.72e-10 -4.69e-07 (-0.286) 1.98e-05 (0.587)
mu -3.97e-08 (-0.029) 4.72e-10 1.47e-07 -5.92e-09 (-0.046) -1.44e-07 (-0.054)
sigma 3.04e-07 (0.249) -4.69e-07 (-0.286) -5.92e-09 (-0.046) 1.13e-07 -3.71e-07 (-0.159)
tau -4.05e-06 (-0.161) 1.98e-05 (0.587) -1.44e-07 (-0.054) -3.71e-07 (-0.159) 4.78e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 87.19 (chi2/ndof = 0.9) │ Nfcn = 110 │\n", "│ EDM = 2.41e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.008 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.3e-3 │ 0.4e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.24e-3 │ 0.34e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.32e-05 -5.02e-06 -3.97e-08 3.04e-07 -4.05e-06 │\n", "│ b │ -5.02e-06 2.37e-05 4.72e-10 -4.69e-07 1.98e-05 │\n", "│ mu │ -3.97e-08 4.72e-10 1.47e-07 -5.92e-09 -1.44e-07 │\n", "│ sigma │ 3.04e-07 -4.69e-07 -5.92e-09 1.13e-07 -3.71e-07 │\n", "│ tau │ -4.05e-06 1.98e-05 -1.44e-07 -3.71e-07 4.78e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 36.34 (chi2/ndof = 0.8) Nfcn = 110
EDM = 1.7e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.008 0.005 0
2 mu 999.3e-3 0.4e-3 0 2
3 sigma 99.20e-3 0.34e-3 0
4 tau 1.002 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.32e-05 -5.06e-06 (-0.285) -4.03e-08 (-0.029) 3.09e-07 (0.250) -4.07e-06 (-0.162)
b -5.06e-06 (-0.285) 2.38e-05 4.78e-10 -4.76e-07 (-0.287) 1.98e-05 (0.588)
mu -4.03e-08 (-0.029) 4.78e-10 1.48e-07 -6.05e-09 (-0.046) -1.46e-07 (-0.055)
sigma 3.09e-07 (0.250) -4.76e-07 (-0.287) -6.05e-09 (-0.046) 1.16e-07 -3.77e-07 (-0.160)
tau -4.07e-06 (-0.162) 1.98e-05 (0.588) -1.46e-07 (-0.055) -3.77e-07 (-0.160) 4.79e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 36.34 (chi2/ndof = 0.8) │ Nfcn = 110 │\n", "│ EDM = 1.7e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.008 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.3e-3 │ 0.4e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.20e-3 │ 0.34e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.32e-05 -5.06e-06 -4.03e-08 3.09e-07 -4.07e-06 │\n", "│ b │ -5.06e-06 2.38e-05 4.78e-10 -4.76e-07 1.98e-05 │\n", "│ mu │ -4.03e-08 4.78e-10 1.48e-07 -6.05e-09 -1.46e-07 │\n", "│ sigma │ 3.09e-07 -4.76e-07 -6.05e-09 1.16e-07 -3.77e-07 │\n", "│ tau │ -4.07e-06 1.98e-05 -1.46e-07 -3.77e-07 4.79e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 7.555 (chi2/ndof = 0.5) Nfcn = 102
EDM = 5.22e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.008 0.005 0
2 mu 999.2e-3 0.4e-3 0 2
3 sigma 99.5e-3 0.4e-3 0
4 tau 1.002 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.34e-05 -5.35e-06 (-0.297) -4.44e-08 (-0.030) 3.52e-07 (0.260) -4.31e-06 (-0.169)
b -5.35e-06 (-0.297) 2.42e-05 -3.86e-10 -5.42e-07 (-0.298) 2.02e-05 (0.591)
mu -4.44e-08 (-0.030) -3.86e-10 1.61e-07 -7.05e-09 (-0.047) -1.64e-07 (-0.059)
sigma 3.52e-07 (0.260) -5.42e-07 (-0.298) -7.05e-09 (-0.047) 1.37e-07 -4.28e-07 (-0.167)
tau -4.31e-06 (-0.169) 2.02e-05 (0.591) -1.64e-07 (-0.059) -4.28e-07 (-0.167) 4.83e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 7.555 (chi2/ndof = 0.5) │ Nfcn = 102 │\n", "│ EDM = 5.22e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.008 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.2e-3 │ 0.4e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.5e-3 │ 0.4e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.34e-05 -5.35e-06 -4.44e-08 3.52e-07 -4.31e-06 │\n", "│ b │ -5.35e-06 2.42e-05 -3.86e-10 -5.42e-07 2.02e-05 │\n", "│ mu │ -4.44e-08 -3.86e-10 1.61e-07 -7.05e-09 -1.64e-07 │\n", "│ sigma │ 3.52e-07 -5.42e-07 -7.05e-09 1.37e-07 -4.28e-07 │\n", "│ tau │ -4.31e-06 2.02e-05 -1.64e-07 -4.28e-07 4.83e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 2.084 (chi2/ndof = 0.4) Nfcn = 114
EDM = 4.67e-08 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.995 0.004 0
1 b 1.007 0.005 0
2 mu 999.4e-3 0.5e-3 0 2
3 sigma 99.8e-3 0.7e-3 0
4 tau 1.003 0.007 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 1.54e-05 -8.38e-06 (-0.397) -7.46e-08 (-0.042) 1.14e-06 (0.413) -6.74e-06 (-0.238)
b -8.38e-06 (-0.397) 2.9e-05 2.16e-08 (0.009) -1.76e-06 (-0.464) 2.43e-05 (0.624)
mu -7.46e-08 (-0.042) 2.16e-08 (0.009) 2.06e-07 -2.27e-08 (-0.071) -2.06e-07 (-0.063)
sigma 1.14e-06 (0.413) -1.76e-06 (-0.464) -2.27e-08 (-0.071) 4.95e-07 -1.39e-06 (-0.274)
tau -6.74e-06 (-0.238) 2.43e-05 (0.624) -2.06e-07 (-0.063) -1.39e-06 (-0.274) 5.22e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 2.084 (chi2/ndof = 0.4) │ Nfcn = 114 │\n", "│ EDM = 4.67e-08 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.995 │ 0.004 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.007 │ 0.005 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 999.4e-3 │ 0.5e-3 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 99.8e-3 │ 0.7e-3 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.003 │ 0.007 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 1.54e-05 -8.38e-06 -7.46e-08 1.14e-06 -6.74e-06 │\n", "│ b │ -8.38e-06 2.9e-05 2.16e-08 -1.76e-06 2.43e-05 │\n", "│ mu │ -7.46e-08 2.16e-08 2.06e-07 -2.27e-08 -2.06e-07 │\n", "│ sigma │ 1.14e-06 -1.76e-06 -2.27e-08 4.95e-07 -1.39e-06 │\n", "│ tau │ -6.74e-06 2.43e-05 -2.06e-07 -1.39e-06 5.22e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 2.676e-06 Nfcn = 112
EDM = 2.68e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 s 0.996 0.005 0
1 b 1.006 0.007 0
2 mu 0.9998 0.0019 0 2
3 sigma 0.1001 0.0012 0
4 tau 1.002 0.008 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
s b mu sigma tau
s 2.3e-05 -1.94e-05 (-0.597) -1.61e-06 (-0.174) 3.75e-06 (0.663) -1.39e-05 (-0.347)
b -1.94e-05 (-0.597) 4.61e-05 5.41e-07 (0.041) -5.47e-06 (-0.682) 3.81e-05 (0.671)
mu -1.61e-06 (-0.174) 5.41e-07 (0.041) 3.71e-06 -6.64e-07 (-0.292) -4.18e-06 (-0.259)
sigma 3.75e-06 (0.663) -5.47e-06 (-0.682) -6.64e-07 (-0.292) 1.39e-06 -3.62e-06 (-0.366)
tau -1.39e-05 (-0.347) 3.81e-05 (0.671) -4.18e-06 (-0.259) -3.62e-06 (-0.366) 7e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 2.676e-06 │ Nfcn = 112 │\n", "│ EDM = 2.68e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ s │ 0.996 │ 0.005 │ │ │ 0 │ │ │\n", "│ 1 │ b │ 1.006 │ 0.007 │ │ │ 0 │ │ │\n", "│ 2 │ mu │ 0.9998 │ 0.0019 │ │ │ 0 │ 2 │ │\n", "│ 3 │ sigma │ 0.1001 │ 0.0012 │ │ │ 0 │ │ │\n", "│ 4 │ tau │ 1.002 │ 0.008 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────┐\n", "│ │ s b mu sigma tau │\n", "├───────┼───────────────────────────────────────────────────┤\n", "│ s │ 2.3e-05 -1.94e-05 -1.61e-06 3.75e-06 -1.39e-05 │\n", "│ b │ -1.94e-05 4.61e-05 5.41e-07 -5.47e-06 3.81e-05 │\n", "│ mu │ -1.61e-06 5.41e-07 3.71e-06 -6.64e-07 -4.18e-06 │\n", "│ sigma │ 3.75e-06 -5.47e-06 -6.64e-07 1.39e-06 -3.62e-06 │\n", "│ tau │ -1.39e-05 3.81e-05 -4.18e-06 -3.62e-06 7e-05 │\n", "└───────┴───────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAA/MAAAMrCAYAAAAFkcLhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAADJf0lEQVR4nOzde1xVdb7/8feG2BtRLgIBoqiUjbe8jFaGU6bJSMZ0cnI6XZyyMh0NG5Xz81bmeKnRNC+oGMfKsEnH6hxzJumoqEcdj3ijmNImzY6NnBDIC26VBJT9+wP3iq1cRDbsC6/n47Efutf67rW/3/2wPuuzvjeTzWazCQAAAAAAeAwfV1cAAAAAAADUDck8AAAAAAAehmQeAAAAAAAPQzIPAAAAAICHIZkHAAAAAMDDkMwDAAAAAOBhSOYBAAAAAPAwN7m6Aq5UXl6uvLw8BQYGymQyubo6AABcF5vNpnPnzik6Olo+PjyXrwoxHgDgqa43zjfpZD4vL08xMTGurgYAADckNzdXbdq0cXU13BIxHgDg6WqL8006mQ8MDJRU8SMFBQW5uDYAAFwfq9WqmJgYI47hWsR4AICnut4436STefuwu6CgIAI9AMDjMHy8esR4AICnqy3OM9EOAAAAAAAPQzIPAAAAAICHIZkHAAAAAMDDNOk58wCAhnf58mWVlZW5uhoex8/PT76+vq6uBgAA1SLG3xhnxXiSeQBAg7DZbMrPz1dRUZGrq+KxQkJCFBUVxUJ3AAC3QoyvP2fEeJJ5AECDsAf5iIgIBQQEkJDWgc1mU3FxsQoLCyVJrVq1cnGNAAD4CTH+xjkzxpPMAwCc7vLly0aQDwsLc3V1PFKzZs0kSYWFhYqIiGDIPQDALRDj689ZMZ4F8AAATmefPxcQEODimng2++/HfEQAgLsgxjuHM2I8PfMAnK79lAxJ0ndzE11cE7gaw+7qh98PgLvoO2WVWprOGe8zJj8shcS4sEZwNWJU/Tjj9yOZBwAAAFC9olxtsUxUgKnkp2Opr0mP/UkKCK94HxBGcg80MpJ5AAAAANUrPqUAU4nGlb6go7bWCjNZ9Z7fUun9oT+V8QuQkvaR0AONiGQegNNF62TFULy8nIoDPK3HFfYpGI3FWVM9+vfvr549e2rx4sVOuR4AeKKjttY6ZIuVbFLfc3ONYfcdTN8rRcul4lPE+yauMeM8MZ5kHoAzFeVKJw//NBRvxZXjPK1HE7J9+3YNGDBAZ86cUUhIiKurAwANIk/hyrOFu7oaQKNytxjPavYAnKMoV0q9yxhy93TpZGnUDumRt6SyYul4VkUZAAAAAPVGMg/AOYpPVSTtj7yl+JL52lneQ+2XfK++a35Usc0irRup4kW91XfKKlfXFKjRhQsX9PTTT6tFixZq1aqVFixY4HD+T3/6k+644w4FBgYqKipKTz75pAoLCyVJ3333nQYMGCBJatmypUwmk5555hlJ0saNG3XPPfcoJCREYWFh+tWvfqVvv/22UdsGAEBT5m0xnmQegFMl/rlQefpp2F2ewhVfMl/jSl9QgKnEYVsbwB1NnDhRO3bs0F/+8hdt3rxZ27dv12effWacLysr0+zZs/X3v/9d69ev13fffWcE85iYGP3nf/6nJOnw4cM6ceKEUlJSJFXcQCQnJ+vAgQPaunWrfHx89Otf/1rl5eWN3kYAAJoib4vxzJkHUH9FudLJI9WezlO4jtpaN2KFgBtz/vx5vfPOO3r//fc1cOBASdKqVavUpk0bo8xzzz1n/P2WW27RkiVLdOedd+r8+fNq0aKFQkNDJUkREREO8+mGDq206rOklStX6uabb9ZXX32l22+/vQFbBQA3oCi3YtSdVGOMryxx6S4dsn0vyXmLkwHO4o0xnp55APVjnyu/bqSKbRadsQW6ukbADfv2229VWlqqPn36GMdCQ0PVsWNH4312drYeeughtW3bVoGBgbrvvvskScePH6/x2t98842eeOIJ3XLLLQoKClL79u2v63MA0OjssX3FfRWv64zxHUzfq6vpmLqajrFODtyON8Z4euYB1E/lufJrfnQYYl+VDqbvKwI8K9vDA124cEEJCQlKSEjQ6tWrdfPNN+v48eNKSEhQaWlpjZ996KGH1K5dO7311luKjo5WeXm5br/99lo/BwCNrlJsV/jPJEnxS/5ebYw/YwtUsc2iFPPynw6mvsZONvAonhjjSeYBOEf4z5Sn76s97RDoU9MJ8HBLt956q/z8/LR37161bdtWknTmzBkdOXJE9913n77++mudOnVKc+fOVUxMxb/fAwcOOFzDbDZLki5fvmwcO3XqlA4fPqy33npL9957ryRp165djdEkALhhiX8u1CFb8yvvqn9Yb18fh33n4c68McaTzANoFPZAf6fP1wR4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39/faddtLFOnTlVycrLx/uzZs2rbti0xHgDgca43ztc5md+5c6fmz5+v7OxsnThxQh9//LGGDBlinK9uKNu8efM0ceJESVL79u31z3/+0+H8nDlzHObBffHFF0pKStL+/ft1880368UXX9SkSZMcPvPRRx/plVde0XfffafbbrtNr7/+uh588MHrbou9rkFBQQR6AIDHcebw8ezsbBUWFqpXr17GscuXL2vnzp1atmyZNm3apNLSUhUVFTn0zhcUFCgqKkqSFBUVdc2q8/bV7iuXuXoF/IKCAgUFBalZs2by9fWVr69vlWXs16iKxWKRxWK55jgxHgDgqWqL83WeaHfhwgX16NFDqampVZ4/ceKEw2vlypUymUwaOnSoQ7lZs2Y5lHvxxReNc1arVYMGDVK7du2UnZ2t+fPna8aMGVqxYoVRZvfu3XriiSc0YsQIff755xoyZIiGDBnisKAOAAC4PgMHDtSXX36pnJwc43XHHXdo2LBhxt/9/Py0detW4zOHDx/W8ePHFRcXJ0mKi4vTl19+6bDqfGZmpoKCgtSlSxejTOVr2MvYr2E2m9W7d2+HMuXl5dq6datRBgAA3EDP/ODBgzV48OBqz1/91Pwvf/mLBgwYoFtuucXheGBgYLVP2FevXq3S0lKtXLlSZrNZXbt2VU5OjhYuXKhRo0ZJklJSUvTAAw8Yvf2zZ89WZmamli1bprS0tLo2CwCAJi0wMFC33367w7HmzZsrLCzMOD5ixAglJycrNDRUQUFBevHFFxUXF6e7775bkjRo0CB16dJFTz31lObNm6f8/HxNmzZNSUlJRq/56NGjtWzZMk2aNEnPPfectm3bpg8//FAZGRnG9yYnJ2v48OG64447dNddd2nx4sW6cOGCnn322Ub6NQAAcH8Nus98QUGBMjIyNGLEiGvOzZ07V2FhYfr5z3+u+fPn69KlS8a5rKws9evXT2az2TiWkJCgw4cP68yZM0aZqlbDrW2lW6vV6vACAADXZ9GiRfrVr36loUOHql+/foqKitK6deuM876+vtqwYYN8fX0VFxen3/72t3r66ac1a9Yso0xsbKwyMjKUmZmpHj16aMGCBXr77beVkJBglHnsscf0xhtvaPr06erZs6dycnK0cePGaxbFAwCgKWvQBfBWrVqlwMBAPfLIIw7Hf//736tXr14KDQ3V7t27NXXq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RatXr7abbxiGUlJS1Lp1a3l5eSkmJkaHDx+2izl58qSGDh0qX19f+fv7Kz4+XiUlJXYxX331lR588EF5enoqNDRU06dPr5LLypUr1b59e3l6eqpz585at25dTYcDAAAkTZ06Vffee698fHwUGBiouLg4HTp0yC7m/PnzSkhIUMuWLdW8eXMNGjRIx44ds4vJz89XbGysvL29FRgYqLFjx+rixYt2MdnZ2erevbs8PDx01113KT09vUo+qampatu2rTw9PRUVFaUdO3bU+ZgBALCyGjfzpaWl6tq1q1JTU6udP336dM2dO1dpaWnavn27mjVrJpvNpvPnz5sxQ4cO1b59+5SZmak1a9Zo8+bNGjVqlDm/uLhY/fr1U1hYmHJycvTWW29p0qRJmj9/vhmzZcsWPf3004qPj9fu3bsVFxenuLg47d27t6ZDAgCg0du0aZMSEhK0bds2ZWZm6sKFC+rXr59KS0vNmDFjxujTTz/VypUrtWnTJh09elRPPPGEOb+iokKxsbEqLy/Xli1btHjxYqWnpyslJcWMycvLU2xsrPr06aPc3FwlJSXphRde0Pr1682Y5cuXKzk5WRMnTtSuXbvUtWtX2Ww2FRYW3pqdAQCABbgYhmHUemEXF61atUpxcXGSLp2VDwkJ0e9+9zv9/ve/lyQVFRUpKChI6enpGjJkiA4cOKCIiAh98cUX6tGjhyQpIyNDjz32mP79738rJCRE7733nv74xz+qoKBA7u7ukqRXXnlFq1ev1sGDByVJgwcPVmlpqdasWWPmc99996lbt25KS0u7ofyLi4vl5+enoqIi+fr61nY3XHI0V5r/sGLL3tA+I/zm1gWgXnV0ydNajz9KozZJId0cnQ5QY3Vav67i+PHjCgwM1KZNm/TQQw+pqKhIt912m5YuXaonn3xSknTw4EF16NBBW7du1X333ae///3v+tWvfqWjR48qKChIkpSWlqY//OEPOn78uNzd3fWHP/xBa9eutfvyfciQITp9+rQyMjIkSVFRUbr33ns1b948SVJlZaVCQ0P10ksv6ZVXXqk237KyMpWVldnto9DQUGo80AhR52F1N1rn6/Se+by8PBUUFCgmJsac5ufnp6ioKG3dulWStHXrVvn7+5uNvCTFxMTI1dVV27dvN2Meeughs5GXJJvNpkOHDunUqVNmzJXbuRxzeTvVKSsr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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def integral(xe, s, b, mu, sigma, tau):\n", " return s * n * norm.cdf(xe, mu, sigma) + b * n * expon.cdf(xe, 0, tau)\n", "\n", "fig, ax = plt.subplots(3, 2, figsize=(10, 8), sharex=True, constrained_layout=True)\n", "for axi, bins in zip(ax.flat, (200, 100, 50, 20, 10, 5)):\n", " w, xe = np.histogram(pts, bins=bins, range=(0, 2))\n", " c = ExtendedBinnedNLL(w, xe, integral)\n", " m = fit(c)\n", " display(m)\n", " axi.stairs(w, xe, fill=True, label=\"data\")\n", " axi.stairs(np.diff(integral(xe, *m.values)), xe, label=\"fit\")\n", " axi.legend()\n", " results[bins] = (np.array(m.values), np.array(m.errors), m.fmin.time)\n", "fig.supxlabel(\"x\");" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABJ4AAAY1CAYAAABjVxTuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdf1hU553//9eAMkNUcFECoiCoiUrSgj8iwU2idGnGH1+jNonWtJWS6K5uSIt8GiP5WNGkXdpu/EEj1TR+LNE0iaYqNSWLJVg1XhKpILuxxqwmRAwCYoyMoIIy8/2DOnEK6Gg4MMDzcV3niuee9zlzn9swM764zz0mh8PhEAAAAAAAANDGvDq6AwAAAAAAAOiaCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAACAbmjfvn2aNm2aQkJCZDKZlJ2dfcP67du369vf/rYCAwPl5+en2NhY7dq1q1ldZmamwsPDZbFYFBMTo8LCQoOuAAAAdAYETwAAAN1QXV2doqKilJmZ6Vb9vn379O1vf1vvvvuuioqKFBcXp2nTpunw4cPOmi1btiglJUVpaWkqLi5WVFSUrFarzpw5Y9RlAAAAD2dyOByOju4EAAAAOo7JZNKOHTs0Y8aMWzrunnvu0ezZs7Vs2TJJUkxMjO677z6tXbtWkmS32xUaGqpnnnlGS5YsaetuAwCATqBHR3egK7Pb7Tp9+rT69Okjk8nU0d0BAACtcDgcunDhgkJCQuTlxYRwd9jtdl24cEEBAQGSpIaGBhUVFSk1NdVZ4+Xlpfj4eBUUFLR6nvr6etXX17uc99y5c+rXrx+fnwAA8GDufn4ieDLQ6dOnFRoa2tHdAAAAbjp16pQGDRrU0d3oFF566SXV1tZq1qxZkqSzZ8+qsbFRQUFBLnVBQUE6duxYq+dJT0/XihUrDO0rAAAwzs0+PxE8GahPnz6Smv4S/Pz8Org3AACgNTabTaGhoc73btzYG2+8oRUrVuiPf/yj7rzzzq91rtTUVKWkpDj3a2pqFBYWxucnAAA8nLufnwieDHRterifnx8fnAAA6AS4tevm3nrrLc2bN09vv/224uPjne39+/eXt7e3qqqqXOqrqqoUHBzc6vnMZrPMZnOzdj4/AQDQOdzs8xOLGAAAAMAtb775phITE/Xmm29q6tSpLo/5+PhozJgxys/Pd7bZ7Xbl5+crNja2vbsKAAA8BDOeAAAAuqHa2lqdOHHCuV9aWqqSkhIFBAQoLCxMqampKi8v16ZNmyQ13V6XkJCgjIwMxcTEqLKyUpLk6+srf39/SVJKSooSEhI0duxYjRs3TmvWrFFdXZ0SExPb/wIBAIBHIHgCAADohg4dOqS4uDjn/rV1lhISEpSVlaWKigqVlZU5H//tb3+rq1ev6umnn9bTTz/tbL9WL0mzZ89WdXW1li1bpsrKSkVHRys3N7fZguMAAKD7MDkcDkdHd6Krstls8vf3V01NDWsUAADgwXjP9hz8XQAA0Dm4+57NGk8AAAAAAAAwBMETAAAAAAAADEHwBAAAAAAAAEMQPAEAAAAAAMAQBE8AAAAAAAAwBMETAADoFC42XFX4khyFL8nRxYarHd0dAAAAuIHgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAAAAAAIbw+OBp3759mjZtmkJCQmQymZSdnX3TY/bs2aPRo0fLbDZr2LBhysrKalaTmZmp8PBwWSwWxcTEqLCw0OXxy5cv6+mnn1a/fv3Uu3dvPfroo6qqqmqjqwIAAAAAAOj6PD54qqurU1RUlDIzM92qLy0t1dSpUxUXF6eSkhIlJydr3rx52rVrl7Nmy5YtSklJUVpamoqLixUVFSWr1aozZ844axYtWqR33nlHb7/9tvbu3avTp0/rO9/5TptfHwAAAAAAQFdlcjgcjo7uhLtMJpN27NihGTNmtFrz3HPPKScnR0eOHHG2ffe739X58+eVm5srSYqJidF9992ntWvXSpLsdrtCQ0P1zDPPaMmSJaqpqVFgYKDeeOMNPfbYY5KkY8eOaeTIkSooKND999/vVn9tNpv8/f1VU1MjPz+/27xqAAAgSRcbripyWdMvko6+YNUdPj3a7Ny8Z3sO/i4AAOgc3H3P9vgZT7eqoKBA8fHxLm1Wq1UFBQWSpIaGBhUVFbnUeHl5KT4+3llTVFSkK1euuNSMGDFCYWFhzpqW1NfXy2azuWwAAAAAAADdVZcLniorKxUUFOTSFhQUJJvNpkuXLuns2bNqbGxssaaystJ5Dh8fH/Xt27fVmpakp6fL39/fuYWGhrbNRQEAAAAAAHRCXS546kipqamqqalxbqdOneroLgEAAAAAAHSYtlscwUMEBwc3+/a5qqoq+fn5ydfXV97e3vL29m6xJjg42HmOhoYGnT9/3mXW0/U1LTGbzTKbzW13MQAAAAAAAJ1Yl5vxFBsbq/z8fJe2vLw8xcbGSpJ8fHw0ZswYlxq73a78/HxnzZgxY9SzZ0+Xmo8//lhlZWXOGgAAAAAAANyYx894qq2t1YkTJ5z7paWlKikpUUBAgMLCwpSamqry8nJt2rRJkrRgwQKtXbtWixcv1pNPPqndu3dr69atysnJcZ4jJSVFCQkJGjt2rMaNG6c1a9aorq5OiYmJkiR/f3899dRTSklJUUBAgPz8/PTMM88oNjbW7W+0AwAAAAAA6O48Png6dOiQ4uLinPspKSmSpISEBGVlZamiokJlZWXOxyMiIpSTk6NFixYpIyNDgwYN0oYNG2S1Wp01s2fPVnV1tZYtW6bKykpFR0crNzfXZcHx1atXy8vLS48++qjq6+tltVr1m9/8ph2uGAAAAAAAoGswORwOR0d3oquy2Wzy9/dXTU2N/Pz8Oro7AAB0ahcbripy2S5J0tEXrLrDp+1+f8Z7tufg7wIAgM7B3ffsLrfGEwAAAAAAADwDwRMAAAAAAAAMQfAEAAAAAAAAQxA8AQAAAAAAwBAETwAAAAAAADAEwRMAAAAAAAAMQfAEAAAAAAAAQxA8AQAAAAAAwBAETwAAAN3Qvn37NG3aNIWEhMhkMik7O/uG9RUVFXriiSd09913y8vLS8nJyc1qsrKyZDKZXDaLxWLMBQAAgE6B4AkAAKAbqqurU1RUlDIzM92qr6+vV2BgoJYuXaqoqKhW6/z8/FRRUeHcTp482VZdBgAAnVCPju4AAAAA2t/kyZM1efJkt+vDw8OVkZEhSdq4cWOrdSaTScHBwV+7fwAAoGtgxhMAAADaTG1trQYPHqzQ0FBNnz5df/vb325YX19fL5vN5rIBAICug+AJAIAOdLHhqsKX5Ch8SY4uNlzt6O4AX8vw4cO1ceNG/fGPf9Trr78uu92u8ePH6/PPP2/1mPT0dPn7+zu30NDQduwxAAAwGsETAAAA2kRsbKzmzp2r6OhoTZgwQdu3b1dgYKBeeeWVVo9JTU1VTU2Nczt16lQ79hgAABiNNZ4AAABgiJ49e2rUqFE6ceJEqzVms1lms7kdewUAANoTM54AAABgiMbGRn344YcaMGBAR3cFAAB0EGY8AQAAdEO1tbUuM5FKS0tVUlKigIAAhYWFKTU1VeXl5dq0aZOzpqSkxHlsdXW1SkpK5OPjo8jISEnSCy+8oPvvv1/Dhg3T+fPn9Z//+Z86efKk5s2b167XBgAAPAfBEwAAQDd06NAhxcXFOfdTUlIkSQkJCcrKylJFRYXKyspcjhk1apTzz0VFRXrjjTc0ePBgffbZZ5KkL7/8UvPnz1dlZaX+6Z/+SWPGjNGBAwecwRQAAOh+CJ4AAAC6oYkTJ8rhcLT6eFZWVrO2G9VL0urVq7V69eqv2zUAANCFsMYTAAAAAAAADEHwBAAAAAAAAEMQPAEAAAAAAMAQBE8AAAAAAAAwBMETAKDNXWy4qvAlOQpfkqOLDVc7ujsAAAAAOgjBEwAAAAAAAAxB8AQAAAAAAABDEDwBAAAAAADAEARPAAAAAAAAMATBEwAAAAAAAAxB8AQAAAAAAABDEDwBAAAAAADAEARPAAAAAAAAMATBEwAAAAAAAAxB8AQAAAAAAABDEDx1Mhcbrip8SY7Cl+ToYsPVju6OR2OsAAAAAADoWARPAAjpAAAAAACGIHgCADcR0AEAAADArSF4AgAAAAAAgCE6RfCUmZmp8PBwWSwWxcTEqLCwsNXaK1eu6IUXXtDQoUNlsVgUFRWl3Nxcl5oLFy4oOTlZgwcPlq+vr8aPH6+//vWvLjW1tbVKSkrSoEGD5Ovrq8jISK1fv96Q6wMAAAAAAOiKPD542rJli1JSUpSWlqbi4mJFRUXJarXqzJkzLdYvXbpUr7zyil5++WUdPXpUCxYs0MyZM3X48GFnzbx585SXl6fNmzfrww8/1MMPP6z4+HiVl5c7a1JSUpSbm6vXX39dH330kZKTk5WUlKSdO3cafs0AAAAAAABdgccHT6tWrdL8+fOVmJjonHV0xx13aOPGjS3Wb968Wc8//7ymTJmiIUOGaOHChZoyZYpWrlwpSbp06ZK2bdumX/3qV3rooYc0bNgwLV++XMOGDdO6deuc5zlw4IASEhI0ceJEhYeH61//9V8VFRV1w9lWAAAAAAAA+IpHB08NDQ0qKipSfHy8s83Ly0vx8fEqKCho8Zj6+npZLBaXNl9fX+3fv1+SdPXqVTU2Nt6wRpLGjx+vnTt3qry8XA6HQ3/5y1/0v//7v3r44Ydb7W99fb1sNpvLBgAAAAAA0F15dPB09uxZNTY2KigoyKU9KChIlZWVLR5jtVq1atUqHT9+XHa7XXl5edq+fbsqKiokSX369FFsbKxefPFFnT59Wo2NjXr99ddVUFDgrJGkl19+WZGRkRo0aJB8fHw0adIkZWZm6qGHHmq1v+np6fL393duoaGhbTAKAAAAAAAAnZNHB0+3IyMjQ3fddZdGjBghHx8fJSUlKTExUV5eX13q5s2b5XA4NHDgQJnNZv3617/WnDlzXGpefvllffDBB9q5c6eKioq0cuVKPf3003rvvfdafe7U1FTV1NQ4t1OnThl6rQAAAAAAAJ6sR0d34Eb69+8vb29vVVVVubRXVVUpODi4xWMCAwOVnZ2ty5cv64svvlBISIiWLFmiIUOGOGuGDh2qvXv3qq6uTjabTQMGDNDs2bOdNZcuXdLzzz+vHTt2aOrUqZKkb37zmyopKdFLL73kcuvf9cxms8xmc1tcOgAAAAAAQKfn0TOefHx8NGbMGOXn5zvb7Ha78vPzFRsbe8NjLRaLBg4cqKtXr2rbtm2aPn16s5pevXppwIAB+vLLL7Vr1y5nzZUrV3TlyhWXGVCS5O3tLbvd3gZXBgAAAAAA0PV59IwnSUpJSVFCQoLGjh2rcePGac2aNaqrq1NiYqIkae7cuRo4cKDS09MlSQcPHlR5ebmio6NVXl6u5cuXy263a/Hixc5z7tq1Sw6HQ8OHD9eJEyf07LPPasSIEc5z+vn5acKECXr22Wfl6+urwYMHa+/evdq0aZNWrVrV/oMAAAAAAADQCXl88DR79mxVV1dr2bJlqqysVHR0tHJzc50LjpeVlbnMTLp8+bKWLl2qTz/9VL1799aUKVO0efNm9e3b11lTU1Oj1NRUff755woICNCjjz6qn//85+rZs6ez5q233lJqaqq+973v6dy5cxo8eLB+/vOfa8GCBe127QAAAAAAAJ2ZxwdPkpSUlKSkpKQWH9uzZ4/L/oQJE3T06NEbnm/WrFmaNWvWDWuCg4P1u9/97pb6CQAAAAAAgK949BpPAAAAAAAA6LwIngAAAAAAAGAIgicAAAAAAAAYguAJAAAAAAAAhiB4AgAAAAAAgCEIngAAAAAAAGAIgicAAAAAAAAYguAJAAAAAAAAhiB4AgAAAAAAgCEIngAAAAAAAGAIgicAAAAAAAAYguAJAAAAAAAAhiB4AgAA6Ib27dunadOmKSQkRCaTSdnZ2Tesr6io0BNPPKG7775bXl5eSk5ObrHu7bff1ogRI2SxWPSNb3xD7777btt3HgAAdBoETwAAAN1QXV2doqKilJmZ6VZ9fX29AgMDtXTpUkVFRbVYc+DAAc2ZM0dPPfWUDh8+rBkzZmjGjBk6cuRIW3YdAAB0Ij06ugMAAABof5MnT9bkyZPdrg8PD1dGRoYkaePGjS3WZGRkaNKkSXr22WclSS+++KLy8vK0du1arV+//ut3GgAAdDrMeAIAAECbKCgoUHx8vEub1WpVQUFBq8fU19fLZrO5bAAAoOsgeAIAAECbqKysVFBQkEtbUFCQKisrWz0mPT1d/v7+zi00NNTobgIAgHZE8AQAAIAOk5qaqpqaGud26tSpju4SAABoQ6zxBAAAgDYRHBysqqoql7aqqioFBwe3eozZbJbZbDa6awAAoIMw4wkAAABtIjY2Vvn5+S5teXl5io2N7aAeAQCAjsaMJwAAgG6otrZWJ06ccO6XlpaqpKREAQEBCgsLU2pqqsrLy7Vp0yZnTUlJifPY6upqlZSUyMfHR5GRkZKkH//4x5owYYJWrlypqVOn6q233tKhQ4f029/+tl2vDQAAeA6CJwAAgG7o0KFDiouLc+6npKRIkhISEpSVlaWKigqVlZW5HDNq1Cjnn4uKivTGG29o8ODB+uyzzyRJ48eP1xtvvKGlS5fq+eef11133aXs7Gzde++9xl8QAADwSARPAAAA3dDEiRPlcDhafTwrK6tZ243qr3n88cf1+OOPf52uAQCALoQ1ngAAAAAAAGAIgicAAAAAAAAYguAJAAAAAAAAhiB4AgAAAAAAgCEIngAAAAAAAGAIgicAAAAAAAAYguAJAAAAAAAAhiB4AgAAAAAAgCEIngAAAAAAAGCIHh3dAQAAAAAAAHc12h0qLD2nMxcu684+Fo2LCJC3l6mju4VWEDwBAAAAAIBOIfdIhVa8c1QVNZedbQP8LUqbFqlJ9w7owJ6hNdxqBwAAOoVGu8P558LScy77AACg68s9UqGFrxe7hE6SVFlzWQtfL1bukYoO6hluhOAJAAB4vNwjFYpftde5/8Pf/VUP/HI3HzABAOgmGu0OrXjnqFr6tdO1thXvHOUXUx6I4AkAAHi0a7/drLLVu7Tz200AALqPwtJzzWY6Xc8hqaLmsgpLz7Vfp+CWThE8ZWZmKjw8XBaLRTExMSosLGy19sqVK3rhhRc0dOhQWSwWRUVFKTc316XmwoULSk5O1uDBg+Xr66vx48frr3/9a7NzffTRR3rkkUfk7++vXr166b777lNZWVmbXx8AAGgZv90EAACSdOZC66HT7dSh/Xh88LRlyxalpKQoLS1NxcXFioqKktVq1ZkzZ1qsX7p0qV555RW9/PLLOnr0qBYsWKCZM2fq8OHDzpp58+YpLy9Pmzdv1ocffqiHH35Y8fHxKi8vd9Z88skneuCBBzRixAjt2bNH//M//6Of/vSnslgshl8zAABowm83AQCAJN3Zx71/i7tbh/bj8cHTqlWrNH/+fCUmJioyMlLr16/XHXfcoY0bN7ZYv3nzZj3//POaMmWKhgwZooULF2rKlClauXKlJOnSpUvatm2bfvWrX+mhhx7SsGHDtHz5cg0bNkzr1q1znuf//t//qylTpuhXv/qVRo0apaFDh+qRRx7RnXfe2S7XDQAA+O0mAABoMi4iQAP8LTK18rhJTd9uNy4ioD27BTd4dPDU0NCgoqIixcfHO9u8vLwUHx+vgoKCFo+pr69vNivJ19dX+/fvlyRdvXpVjY2NN6yx2+3KycnR3XffLavVqjvvvFMxMTHKzs5uw6sDAAA3w283AQCAJHl7mZQ2LVKSmoVP1/bTpkXK26u1aAodxZDg6dKlS7p48aJz/+TJk1qzZo3+/Oc/39J5zp49q8bGRgUFBbm0BwUFqbKyssVjrFarVq1apePHj8tutysvL0/bt29XRUXTwqN9+vRRbGysXnzxRZ0+fVqNjY16/fXXVVBQ4Kw5c+aMamtr9Ytf/EKTJk3Sn//8Z82cOVPf+c53tHfv3hafV2oKvWw2m8sGAABuH7/dBAAA10y6d4DWfX+0gv1df+EU7G/Ruu+P1qR7B3RQz3AjPYw46fTp0/Wd73xHCxYs0Pnz5xUTE6OePXvq7NmzWrVqlRYuXGjE00qSMjIyNH/+fI0YMUImk0lDhw5VYmKiy615mzdv1pNPPqmBAwfK29tbo0eP1pw5c1RUVCSpacbTtetYtGiRJCk6OloHDhzQ+vXrNWHChBafOz09XStWrDDs2gAA6G6u/XZz4evFMkkui4zz200AALqfSfcO0Lcjg1VYek5nLlzWnX2afgHFZwHPZciMp+LiYj344IOSpD/84Q8KCgrSyZMntWnTJv361792+zz9+/eXt7e3qqqqXNqrqqoUHBzc4jGBgYHKzs5WXV2dTp48qWPHjql3794aMmSIs2bo0KHau3evamtrderUKRUWFurKlSvOmv79+6tHjx6KjIx0OffIkSNv+K12qampqqmpcW6nTp1y+1oBAEDLrv12804/s0s7v90EAKB78vYyKXZoP02PHqjYof0InTycIcHTxYsX1adPH0nSn//8Z33nO9+Rl5eX7r//fp08edLt8/j4+GjMmDHKz893ttntduXn5ys2NvaGx1osFg0cOFBXr17Vtm3bNH369GY1vXr10oABA/Tll19q165dzhofHx/dd999+vjjj13q//d//1eDBw9u9TnNZrP8/PxcNgAA8PVNuneA3kv5asZxVuJ92v/ctwidAAAAPJwht9oNGzZM2dnZmjlzpnbt2uW8Xe3MmTO3HMakpKQoISFBY8eO1bhx47RmzRrV1dUpMTFRkjR37lwNHDhQ6enpkqSDBw+qvLxc0dHRKi8v1/Lly2W327V48WLnOXft2iWHw6Hhw4frxIkTevbZZzVixAjnOSXp2Wef1ezZs/XQQw8pLi5Oubm5euedd7Rnz56vOToAAOB2XP/bTKbUAwAAdA6GBE/Lli3TE088oUWLFulf/uVfnLOT/vznP2vUqFG3dK7Zs2erurpay5YtU2VlpaKjo5Wbm+tccLysrExeXl9N3Lp8+bKWLl2qTz/9VL1799aUKVO0efNm9e3b11lTU1Oj1NRUff755woICNCjjz6qn//85+rZs6ezZubMmVq/fr3S09P1ox/9SMOHD9e2bdv0wAMPfI2RAQAAAAAA6D4MCZ4ee+wxPfDAA6qoqFBUVJSz/V/+5V80c+bMWz5fUlKSkpKSWnzsH2cgTZgwQUePHr3h+WbNmqVZs2bd9HmffPJJPfnkk273EwAAAAAAAF8xJHiSpODg4GYLgI8bN86opwMAAAAAAICHMWRxcQAAAAAAAIDg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AAAAAIYgeAIAAAAAAIAhCJ4AAAAAAABgCIInAAAAAAAAGILgCQAAoBvat2+fpk2bppCQEJlMJmVnZ9/0mD179mj06NEym80aNmyYsrKyXB5fvny5TCaTyzZixAhjLgAAAHQKBE8AAADdUF1dnaKiopSZmelWfWlpqaZOnaq4uDiVlJQoOTlZ8+bN065du1zq7rnnHlVUVDi3/fv3G9F9AADQSfTo6A4AAACg/U2ePFmTJ092u379+vWKiIjQypUrJUkjR47U/v37tXr1almtVmddjx49FBwc/LX719DQoIaGhmbtXl5e6tGjh0tda0wmk3r27HlbtVeuXJHD4WjXWkny8fG5rdqrV6/Kbre3SW3Pnj1lMpkMrW1sbFRjY2Ob1Pbo0UNeXl4eU2u323X16tVWa729veXt7e0xtQ6HQ1euXGmT2ut/Po2qlW78s8xrRMu1vEbwGmHEa8SNfmauR/AEAACAmyooKFB8fLxLm9VqVXJyskvb8ePHFRISIovFotjYWKWnpyssLKzV89bX16u+vt65b7PZJEkrV66UxWJpVn/XXXfpiSeecO6/9NJLrX4gHjx4sH74wx869zMyMnTx4sUWa0NCQjR//nznfmZmpmpqalqsDQwM1L//+78791999VVVV1e3WOvv7+8yRllZWTp9+nSLtXfccYeeffZZ5/7vf/97nTx5ssXanj176vnnn3fub926VcePH2+xVpLS0tKcf96xY4eOHj3aam1qaqrzH6F/+tOf9N///d+t1v7kJz9Rr169JEm7du3SoUOHWq398Y9/rL59+0qS8vPzVVBQ0GrtwoULdeedd0qS3n//fe3du7fV2nnz5mngwIGSpA8++EDvvfdeq7UJCQkKDw+XJBUVFem//uu/Wq2dM2eO7r77bknShx9+qD/+8Y+t1j722GO65557JEkfffSR/vCHP7RaO336dEVHR0uSTpw4oTfffLPV2smTJ2vcuHGSpLKyMr322mut1sbHx+uf//mfJUkVFRXasGFDq7UTJkzQxIkTJUnV1dVat25dq7WxsbF6+OGHJUk1NTXKyMhotXbs2LGaOnWqJOnixYt66aWXWq2NiorSjBkzJDUFKOnp6a3WRkZG6vHHH3fu36iW14gmvEZ8hdeIJka8Rly+fLnVc1yPW+0AAABwU5WVlQoKCnJpCwoKks1m06VLlyRJMTEx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BQtks9k0YMAApaenuyYcz8vLcxuZdObMGc2fP1+HDx9W586dNWbMGK1Zs8btcriSkhIlJSXp+PHjCg4O1oQJE/SnP/1Jvr6+rpp169YpKSlJd911l7777jtFRkbqT3/6k6ZPn+6qWbZsmYxGoyZMmKCKigolJCToueee8/xJAQAAtQr0DdT+xP3e7gYAAAAugsHpdDq93Ym2ym63KygoSCUlJTKZTN7uDgAAqAPv2S0H3wsAAFqHhr5nt6q72gEAAAAAAKD1IHgCAAAAAACARxA8AQAAAAAAwCMIngAAAAAAAOARBE8AAAAAAADwCIInAAAAAAAAeATBEwAAQDuVmpqqqKgoBQQEKC4uTnv37q2ztqqqSo899ph69eqlgIAAxcTEKD093a3m0UcflcFgcFuio6M9fRgAAKAFI3gCAABoh9avXy+r1aqFCxcqJydHMTExSkhIUFFRUa318+fP1wsvvKBnn31Wn3/+uaZPn65bb71V+/btc6u79tprVVBQ4Fp27drVHIcDAABaKIInAACAdiglJUXTpk3TlClT1K9fP61atUqBgYFavXp1rfVr1qzRvHnzNGbMGF111VW67777NGbMGC1dutStrkOHDgoNDXUt3bp1a47DAQAALVQHb3egLXM6nZIku93u5Z4AAID6nHuvPvfe3dZVVlYqOztbSUlJrm1Go1EWi0WZmZm17lNRUaGAgAC3bR07djxvRNPBgwcVFhamgIAAxcfHKzk5WREREXX2paKiQhUVFa71kpISSXx+AgCgpWvo5yeCJw86deqUJCk8PNzLPQEAAA1x6tQpBQUFebsbHvfNN9+opqZGZrPZbbvZbNaBAwdq3SchIUEpKSm64YYb1KtXL2VkZGjjxo2qqalx1cTFxSktLU19+vRRQUGBFi1apJ///Of69NNP1aVLl1rbTU5O1qJFi87bzucnAABahwt9fjI428uf9rzA4XDoxIkT6tKliwwGQ5O1a7fbFR4ermPHjslkMjVZu20R56rhOFcNw3lqOM5Vw3GuGs5T58rpdOrUqVMKCwuT0dj2ZyI4ceKEevbsqd27dys+Pt61/eGHH9aOHTu0Z8+e8/YpLi7WtGnT9O6778pgMKhXr16yWCxavXq1Tp8+XevznDx5UpGRkUpJSdG9995ba81PRzw5HA599913uvzyy/n85CWcq4bhPDUc56rh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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "npar = len(results[np.inf][0])\n", "\n", "fig, ax = plt.subplots(npar, 2, sharex=True, figsize=(14, 20))\n", "for j, (k, (v, e, _)) in enumerate(results.items()):\n", " for i, (vi, ei) in enumerate(zip(v, e)):\n", " c = f\"C{i}\"\n", " ax[i, 0].errorbar(j, vi, ei, color=c, fmt=\"o\")\n", " ax[i, 0].set_ylabel(par_names[i])\n", " einf = results[np.inf][1][i]\n", " ax[i, 1].plot(j, ei /einf, \"o\", color=c)\n", "for i in range(npar):\n", " ax[i, 1].set_ylim(0.95, 1.2)\n", " ax[i, 1].axhline(1.05, ls=\"--\", color=\"0.5\")\n", "plt.xticks(np.arange(7), [f\"{x}\" for x in results.keys()]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Shown on the left is the fitted value and its uncertainty estimate. Shown of the right is the relative size of the error bar of the binned fit compared to the unbinned fit." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/png": "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aWmpwsLCVFJSotDQUF+XAwAA3FCX72+/HiMEAADgSQQhAABgWQQhAABgWQQhAABgWQQhAABgWQQhAABgWQShGtjtdiUmJio1NdXXpQAAAA9iHqFaMI8QAACND/MIAQAAuIEgBAAALIsgBAAALIsgBAAALIsgBAAALIsgBAAALIsgVAPmEQIAwBqYR6gWzCMEAEDjwzxCAAAAbiAIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyII1YCZpQEAsAZmlq4FM0sDAND4MLM0AACAGwhCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCNeARGwAAWAOP2KgFj9gAAKDx4REbAAAAbiAIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyII1YCnzwMAYA08fb4WPH0eAIDGh6fPAwAAuIEgBAAALKteQaikpETFxcXVlhcXF6u0tPSSiwIAAPCGegWhe+65R2+99Va15W+//bbuueeeSy4KAADAG+oVhLZt26a+fftWW96nTx9t27btkosCAADwhnoFoYqKCp09e7ba8jNnzujHH3+85KIAAAC8oV5BqHv37nr11VerLV+wYIFSUlIuuSgAAABvCKrPh5555hmlpaXp888/1y233CJJ2rBhgz777DP97//+b4MWCAAA4Cn16hG66aablJOTo/bt2+vtt9/WmjVr9LOf/Ux///vf9ctf/rKhawQAAPAIZpauBTNLAwDQ+HhlZun9+/dr2rRpuu+++1RUVCRJ+uCDD/TFF1/Ud5MAAABeVa8gtHHjRnXt2lXbtm3Tu+++q/LycknS559/rszMzAYtEAAAwFPqFYSmTp2qZ555Rh9++KGaN2/uWn7zzTdr69atDVYcAACAJ9UrCOXn52vIkCHVlrdr107Hjh275KIAAAC8oV5BKDw8XAUFBdWW5+bm6oorrrjkogAAALyh3s8amzJligoLC2Wz2eR0OrV582ZNmjRJI0aMaOgaAQAAPKJeQei5555T586dFRcXp/LyciUmJupXv/qVbrzxRk2bNq2ha/Q6u92uxMREpaam+roUAADgQZc0j9Dhw4eVn5+v8vJydevWTVdddVVD1uZz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EN1xxx11+lzlLNzJycmaOnWqXnrpJf35z39mFm4AlkMQAhqx119/Xffff79atGhR589WzsL97//+77r77ruZhRuAJdEHDr/mcBrl7P9eq/K+Vc7+7+VwcpNjpU8++UR79+51Xd6qj6YwC/emTZs0cOBAxcbGymazaeXKlVXWl5eXa/z48Wrfvr1atmypxMRELViwoEqb/fv3a8iQIWrbtq1CQ0N111136ejRo17cCwC+QhCC31q3q0C9Zmfp3j9t1YS38nTvn7aq1+wsrdtV4OvS/MJrr72mlJQUJSUl1evzTWUW7ovdZZeRkaF169ZpyZIl+sc//qGJEydq/PjxWr16tevzt912m2w2m7KysrR582adPn1aAwcOdD3jDkDTRRCCX1q3q0BjluxUQcmpKssLS05pzJKdTToMlZeXux41Iv1rFu6fPlOttLRUy5Ytu2Bv0KFDh1yfcTgcru2Vl5dLqnkW7ueee05bt271+P41tPT0dD3zzDMaMmRIjeu3bNmikSNHqk+fPurYsaMefvhhJSUl6dNPP5Ukbd68WQcPHtSiRYvUtWtXde3aVYsXL9b27duVlZUlSXrjjTfUunVrffXVV67tjh07Vp07d9bJkyc9v5OAH7tYr+zRo0c1atQoxcbGqlWrVurfv3+Vc+mnjDFKT0+vcTueQhCC33E4jWau2a2aLoJVLpu5ZneTvUy2fft2devWTd26dZN0rkejW7dumj59uqvNW2+9JWOM7r333hq3MX36dHXr1k2ZmZkqLy93bW/79u0XnYW7Miw1FTfeeKNWr16tb7/9VsYYZWdn68svv9Rtt90mSaqoqJDNZlNwcLDrMy1atFBAQID+9re/SZJGjBihAQMG6P7779fZs2e1du1a/fnPf9Zf/vIXtWrVyif7BfiL2npljTEaPHiwvv76a61atUq5ubnq0KGD0tLSdOLEiWrt58+ff8FJYz3G4IJKSkqMJFNSUuLrUixly75jpsOU9y762rLvmK9LhZ+RZFasWFFl2alTp8yIESOMJBMUFGSaN29uFi9e7FpfVFRkQkNDzYQJE8yJEydMeXm5GT9+vJFkHn74YVe74uJi0759ezNmzBgTFRVlnn32WW/tFnxg48aN5te//rWJiYmp8feqsLDQjBw50sTExJiWLVuafv36mS+//LJKm3379pnBgwebNm3amJCQEDN0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x48f144dO1xtsrKy5HQ61aNHD6/X7Evl5eXav3+/YmJi3Po9s5KGOC9xjjvnJf7lp+elt3FpDD4xbtw4/fWvf9WqVasUEhLiumYeFhamli1bKiwsTA888IAyMjIUGRmp0NBQPfroo+rZs6duuOEGSdJtt92mxMREDR8+XM8//7wKCws1bdo0jRs3rsk99bm8vLzK/5QOHDigvLw8RUZGKj4+XhMnTtQzzzyjq666SgkJCXr66acVGxurwYMHS5J+8YtfqH///nrooYe0YMECnTlzRuPHj9c999yj2NhYH+2Vd0yaNEkDBw5Uhw4ddOTIEWVmZiowMFD33nuvW79nVtIQ56WVXOp5aWW1nZde5+vb1pqy82+ff+GFF6qsT0pKqnKb/PnvmzKdN11A5WvhwoWuNj/++KMZO3asiYiIMK1atTJDhgwxBQUFVbZz8OBBk56eblq2bGnatGljnnjiCXPmzBkv743nZWdn13i8Ro4caYw5d6vu008/baKiokxwcLC55ZZbzN69e6ts4/vvvzf33nuvad26tQkNDTWjR482ZWVlPtgb77r77rtNTEyMad68ubniiivM3Xffbfbt2+da787vmVU01HlpFQ1xXlrVxc5Lb7IZY4w3gxcAAIC/YIwQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwrP8DNT/qmdCRMZ0AAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure()\n", "x = np.arange(7)\n", "y = [v[2] for v in results.values()]\n", "plt.plot(x, y, \"o\")\n", "for xi, yi in zip(x[1:], y[1:]):\n", " plt.text(xi, yi * 1.2, f\"{y[0]/yi:.0f}x\", ha=\"center\")\n", "plt.xticks(x, [f\"{x}\" for x in results.keys()])\n", "plt.ylabel(\"time / sec\")\n", "plt.semilogy();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now demonstrate that the binned fits and the unbinned fit are unbiased. We repeat the fit many times with independent random samples, the mean of the results minus the truth is the bias. In each iteration, the binned fits use the same data that the unbinned fit uses." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "@joblib.delayed\n", "def run(seed):\n", " rng = np.random.default_rng(seed)\n", " s = rng.normal(truth[2], truth[3], size=int(n * truth[0]))\n", " b = rng.exponential(truth[4], size=int(n * truth[1]))\n", " pts = np.append(s, b)\n", " pts = pts[(pts > 0) & (pts < 2)]\n", "\n", " if bins == np.inf:\n", " m = fit(ExtendedUnbinnedNLL(pts, density))\n", " assert m.valid\n", " else:\n", " w, xe = np.histogram(pts, bins=bins, range=(0, 2))\n", " m = fit(ExtendedBinnedNLL(w, xe, integral))\n", " assert m.valid\n", " return np.array(m.values)\n", "\n", "results = {}\n", "for bins in (np.inf, 200, 100, 50, 20, 10, 5):\n", " results[bins] = joblib.Parallel(-1)(run(seed) for seed in range(100))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGzCAYAAADnmPfhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAn6ElEQVR4nO3de1RVdf7/8dfhLiooSoDKVzBNJfGShoNa0sQKzSmcGqcsRRnTCcWlYZOXr3mrGZsxL9RoNrYanJ/mlDOl9rU0sjRHSfLCdzQvqanYCIhjgpcEgfP7wy9nPMFBQA6HDz4fa50V+3Pee/PG3fnwYu99zrZYrVarAAAADOHm6gYAAABqgvACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AKgWubMmSOLxaJz585VWRcWFqbRo0fXT1MAbkuEFwBGu3LlipYuXaqHHnpIISEhat68uXr16qU33nhDpaWlFerLysr0hz/8QeHh4fLx8VH37t21Zs2aSrd96NAhDRo0SM2aNVNAQIBGjhyp/Px8Z/9IAG7Cw9UNAGhcjhw5Ije3+vu76Ntvv9XEiRP14IMPKiUlRX5+ftq8ebPGjx+vL7/8UitXrrSr/+///m+98sorGjt2rO69916tX79eTz31lCwWi5588klb3Xfffaf7779f/v7++t3vfqdLly7p1Vdf1f79+5WZmSkvL696+xkB/IgVAKph9uzZVknW/Px8V7diJz8/33rgwIEK44mJiVZJ1qNHj9rGvvvuO6unp6d1woQJtrGysjLrfffdZ23Xrp21pKTENp6UlGRt0qSJ9dSpU7ax9PR0qyTrm2++6aSfBkB1cNoIQI2cO3dOv/zlL+Xn56dWrVpp0qRJunr1qu35H1/zkpaWJovFoh07diglJUWBgYFq2rSpfv7zn9fJKZjWrVvr7rvvrjD+85//XNL1Uz/l1q9fr2vXrmn8+PG2MYvFoqSkJH333XfKyMiwjf/973/Xz372M/3Xf/2XbSw2NlZ33XWX3nvvvVvuG0DtcdoIQI388pe/VFhYmObPn68vv/xSr732mr7//nv95S9/qXK9iRMnqmXLlpo9e7ZOnjypJUuWKDk5We+++66t5tKlS3ZByBFPT0/5+/tXWZObmyvpergpt2/fPjVt2lRdu3a1q42KirI9P2DAAP3rX//S2bNn1adPnwrbjYqK0kcffXTTHgE4D+EFQI2Eh4dr/fr1kqQJEybIz89Py5Yt0/PPP6/u3bs7XK9Vq1b65JNPZLFYJF2/cPa1115TQUGBLYgkJydXuEalMgMHDtTWrVsdPl9cXKwlS5YoPDxc9957r208JydHQUFBth7KhYSESJLOnDljq7tx/Me158+fV1FRkby9vW/aK4C6R3gBUCMTJkywW544caKWLVumjz76qMrwMm7cOLvQcN9992nx4sU6deqUbb0XXnhBI0aMuGkPLVu2rPL55ORkHTx4UBs3bpSHx3+muR9++KHSwOHj42N7/sb/3qyW8AK4BuEFQI106tTJbvnOO++Um5ubTp48WeV6N147Iv0ngHz//fe2sYiICEVERNxSfwsWLNCKFSv00ksv6eGHH7Z7rkmTJioqKqqwTvmpqiZNmtj9tzq1AOof4QXALfnxKRhH3N3dKx23Wq22rwsKCmxHPari5eWlgICACuNpaWmaOnWqnn32Wc2cObPC8yEhIfr8889ltVrt+i4/TdSmTRtb3Y3jN8rJyVFAQABHXQAX4t1GAGrk6NGjdsvHjh1TWVmZwsLCbnnbkyZNUkhIyE0fjz32WIV1169fr2eeeUaPPfaYli5dWun2e/bsqStXrti9A0mSdu3aZXtektq2bavAwEDt3r27wjYyMzNtdQBcgyMvAGqk/NNsy73++uuSpMGDB9/ytmt7zcsXX3yhJ598Uvfff79Wr17t8EPy4uPj9dxzz2nZsmX64x//KOn6kZ/ly5erbdu26tevn6328ccf18qVK3X69GmFhoZKkrZs2aJvvvlGzz33XG1/RAB1gPACoEZOnDihRx99VIMGDVJGRoZWrVqlp556Sj169LjlbdfmmpdTp07p0UcflcVi0S9+8QutXbvW7vnu3bvbLghu166dJk+erAULFujatWu69957tW7dOm3fvl2rV6+2O7U1Y8YMrV27Vg888IAmTZqkS5cuacGCBYqMjFRiYuIt/6wAao/wAqBG3n33Xc2aNUvTpk2Th4eHkpOTtWDBApf1c+LECRUUFEiq+E4oSZo9e7bdu6BeeeUVtWzZUm+++abS0tLUqVMnWwC7UWhoqLZt26aUlBRNmzZNXl5eGjJkiBYuXMj1LoCLWaw3Xi0HAADQwHHBLgAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAURrd57yUlZXpzJkzat68ebXvuQIAAFzLarXq4sWLatOmjcNPyS7X6MLLmTNnbB/lDQAAzHL69Gm1a9euyppGF16aN28u6foP7+fn5+JuAABAdRQWFio0NNT2e7wqjS68lJ8q8vPzI7wAAGCY6lzywQW7AADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeMFt40pxicKmbVTYtI26Ulzi6nYAALVEeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACM4tTw8sUXX+iRRx5RmzZtZLFYtG7dupuus3XrVt1zzz3y9vZWx44dlZaW5swWAQCAYZwaXi5fvqwePXpo6dKl1ao/ceKEhgwZogceeEBZWVmaPHmynnnmGW3evNmZbQIAAIN4OHPjgwcP1uDBg6tdv3z5coWHh2vhwoWSpK5du+of//iHFi9erLi4OGe1CQAAquFKcYkiZl0/oHBwXpx8vZwaIxxqUNe8ZGRkKDY21m4sLi5OGRkZDtcpKipSYWGh3QMAADReDSq85ObmKigoyG4sKChIhYWF+uGHHypdZ/78+fL397c9QkND66NVAPXsSnGJwqZtVNi0jbpSXOLqdgC4UIMKL7Uxffp0FRQU2B6nT592dUsAAMCJXHOyyoHg4GDl5eXZjeXl5cnPz09NmjSpdB1vb295e3vXR3sAAKABaFBHXqKjo7Vlyxa7sfT0dEVHR7uoIwAA0NA4NbxcunRJWVlZysrKknT9rdBZWVnKzs6WdP2UT0JCgq3+2Wef1bfffqsXXnhBhw8f1rJly/Tee+/pueeec2abAADAIE4NL7t371avXr3Uq1cvSVJKSop69eqlWbNmSZJycnJsQUaSwsPDtXHjRqWnp6tHjx5auHCh3nrrLd4mDQAAbJx6zUtMTIysVqvD5yv79NyYmBjt27fPiV0BAACTNahrXgAAAG6G8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwCgXnBncNQVwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4qSbuyQEAQMNAeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGqZfwsnTpUoWFhcnHx0d9+/ZVZmamw9q0tDRZLBa7h4+PT320CQAADOD08PLuu+8qJSVFs2fP1t69e9WjRw/FxcXp7NmzDtfx8/NTTk6O7XHq1ClntwkAAAzh9PCyaNEijR07VomJiYqIiNDy5cvl6+urt99+2+E6FotFwcHBtkdQUJCz2wQAAIZwangpLi7Wnj17FBsb+59v6Oam2NhYZWRkOFzv0qVLat++vUJDQxUfH6+vv/7aYW1RUZEKCwvtHgAAoPFyang5d+6cSktLKxw5CQoKUm5ubqXrdO7cWW+//bbWr1+vVatWqaysTP369dN3331Xaf38+fPl7+9ve4SGhtb5zwEAABqOBvduo+joaCUkJKhnz54aOHCg3n//fQUGBurNN9+stH769OkqKCiwPU6fPl3PHQMAgPrk4cyNt27dWu7u7srLy7Mbz8vLU3BwcLW24enpqV69eunYsWOVPu/t7S1vb+9b7hUAAJjBqUdevLy81Lt3b23ZssU2VlZWpi1btig6Orpa2ygtLdX+/fsVEhLirDYBAIBBnHrkRZJSUlI0atQo9enTR1FRUVqyZIkuX76sxMRESVJCQoLatm2r+fPnS5LmzZunn/zkJ+rYsaMuXLigBQsW6NSpU3rmmWec3SoAADCA08PLE088ofz8fM2aNUu5ubnq2bOnNm3aZLuINzs7W25u/zkA9P3332vs2LHKzc1Vy5Yt1bt3b+3cuVMRERHObhUAABjA6eFFkpKTk5WcnFzpc1u3brVbXrx4sRYvXlwPXQEAABM1uHcbAQAAVIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QW3jdIyq+3rzBPn7ZYBAOYgvOC2sOlAjmIXbbMtj/7zVxrw+8+06UCOC7sCANQG4QWN3qYDOUpatVd5hUV247kFV5W0ai8BBgAMQ3hBo1ZaZtXcDw+qshNE5WNzPzzIKSQAMAjhBY1a5onzyim46vB5q6ScgqvKPHG+/poCANwSwgsatbMXHQeX2tQBAFyP8IJG7Y7mPnVaBwBwPcILGrWo8ACF+PvI4uB5i6QQfx9FhQfUZ1sAgFtAeEGj5u5m0exHIiSpQoApX579SITc3RzFGwBAQ0N4QaM3qFuI3hhxj+7w87YbD/b30Rsj7tGgbiEu6gwAUBserm4AqA+DuoWof8fWipzziSQpLfFe3dcpkCMuAGAgjrzgtnFjUIkKDyC4AIChCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAQLWUllltX2eeOG+3XJ8ILwCM0FAmTeB2telAjmIXbbMtj/7zVxrw+8+06UBOvfdSL+Fl6dKlCgsLk4+Pj/r27avMzMwq69euXasuXbrIx8dHkZGR+uijj+qjTQANVEOaNIHb0aYDOUpatVd5hUV247kFV5W0am+9vxadHl7effddpaSkaPbs2dq7d6969OihuLg4nT17ttL6nTt3avjw4RozZoz27dunoUOHaujQoTpw4ICzWwXQADW0SRO43ZSWWTX3w4Oq7Fhn+djcDw/W69FQp9+YcdGiRRo7dqwSExMlScuXL9fGjRv19ttva9q0aRXqU1NTNWjQIP3mN7+RJL300ktKT0/XH//4Ry1fvrza37e4uFjFxcUVxt3c3OTh4WFX54jFYpGnp6dt2UOltnU8VFZl7bVr12S1Vr4jnVUrSV5eXrWqLSkpUVlZWZ3Uenp6ymKxOLW2tLRUpaWlNaotLi6pdB96eHjIzc2tWtutj9qysjKVlJQ4rHV3d5e7u3uDqbVarbp27Vqd1N74+rRarbpaVKw5G752OGladH3SjO0apLJSx/3W5HV/K3NETWpvxzmi/DVYcsPfzQ1pjnDkdp8jdp04r5yCqw63YZWUU3BVO4/mqW94QIXt1mSOqC6nhpfi4mLt2bNH06dPt425ubkpNjZWGRkZla6TkZGhlJQUu7G4uDitW7eu0vqioiIVFf3nL7LCwkJJ0sKFC+Xj41OhvlOnTnrqqadsy6+++qrDf9T27dtr9OjRtuVhPvvlYylR6sJ9FWrbtGmjsWPH2paXLl2qgoKCSrcbGBio8ePH25ZXrFih/Pz8Smv9/f01efJk23JaWprOnDlTaa2vr68t9EnS6tWrderUqUprPT09NWPGDNvye++9p6NHj1ZaK0mzZ8+2ff3BBx/o4MGDDmunT59um8j+53/+R//7v//rsPb5559X06ZNJUmbN2/W7t27HdZOmjRJLVq0kCRt2bLF4f9DkpSUlKQ77rhDkrR9+3Zt23b9lMPIJtefv3EfPvPMM2rbtq0k6csvv9Snn37qcLujRo1SWFiYJGnPnj36+OOPHdYOHz5cd911lyRp//79Wr9+vcPaX/ziF7r77rslSYcOHdLf/vY3h7Xx8fHq2bOnJOnYsWNas2aNw9rBgwcrKipKkpSdna2VK1c6rI2NjVX//v0lSTk5OXrrrbcc1g4cOFAxMTGSpPz8fL3xxhsOa6Ojo/XQQw9JkgoKCpSamuqwtk+fPhoyZIgk6cqVK5ry+zeVW9zZYX35pJlx7Kw+++ubDusiIiI0bNgw2/L8+fMd1t7KHJGamqorV65UWssccd3IJtL/+6GXbbmhzRGVud3niG9LAiR1cLiNcm+tWqvPPM5Lqv0cUV1OPW107tw5lZaWKigoyG48KChIubm5la6Tm5tbo/r58+fL39/f9ggNDa2b5gG43A9Wz5sXScq/WHTzIgC10sTi+KhJberqgsVa1THDW3TmzBm1bdtWO3fuVHR0tG38hRde0LZt27Rr164K63h5eWnlypUaPny4bWzZsmWaO3eu8vLyKtRXduQlNDRU+fn58vPzq1Bf20PCV4pL1H3W9QuHd8+Mla+Xh8Na6fY8JFyuoZ42ulJcoj4vX/+L6cZ9eLsfEr7VWmeeNvrHN3ka+ec9DuvLrRnbV71DK77eK9uuxGmj2tTWxRxR/hoskZsOzhskXy+PBjVHOHK7zxGlZVY9sGi78gqLKj2Fa5EU7Oetz1Luk7ubpcJ2qztHFBYWyt/fXwUFBZX+/r6RU08btW7dWu7u7hVCR15enoKDgytdJzg4uEb13t7e8vb2rjDu5eVl92JypDo15UrkfsO2q/6nu3EyuZmGUHvjZG1CbU3OkZbXlsjtpvuwNtut61o3N7dq/3/ZEGotFovTavt1ClKIv49yC646njT9fRQV3so2aVZHTV73zqptCK/7+p4jbnwN1uV2K9MQXsuNaY6Y8+jdSlq1VxbJ7rVY/qqb/ejdauJT8XexVLPXfXU59bSRl5eXevfurS1bttjGysrKtGXLFrsjMTeKjo62q5ek9PR0h/UAGi93N4tmPxIh6T+TZDnbpPlIRI2CC4CaG9QtRG+MuEd3+NkHlGB/H70x4h4N6hZSr/04/a3SKSkpWrFihVauXKlDhw4pKSlJly9ftr37KCEhwe6C3kmTJmnTpk1auHChDh8+rDlz5mj37t1KTk52dqsAGqCGNmkCt6tB3UL0acpA23Ja4r36x9SfuuQ16PS3Sj/xxBPKz8/XrFmzlJubq549e2rTpk22i3Kzs7Nt5/EkqV+/fnrnnXc0c+ZMzZgxQ506ddK6devUrVs3Z7cKoIEa1C1E/Tu2VuScTyRdnzTv6xTIERegnt34mosKD3DZa9Dp4UWSkpOTHR452bp1a4WxYcOG2b2tEQAayqQJwPW4txEAADAK4QUAABiF8AIAqBfcGRx1hfACAHA67gyOukR4AQA4FXcGR10jvAAAnKa0zKq5Hx50eGdw6fqdwTmFhJogvAAAnCbzxHnlFFx1+Hz5ncEzT5yvv6ZgPMILAMBpzl50HFxqUwdIhBcAgBPd0dynTusAifACAHCiqPAAhfj7VLixZjmLpBB/H0WFB9RnWzAc4QUA4DTcGRzOQHgBADgVdwZHXauXGzMCAG5v3BkcdYkjLwCAesGdwVFXCC8AAMAohJdq4oZiAAA0DISXauCGYgAANByEl5vghmIAADQshJcqcEMxAAAaHsJLFbihGAAADQ/hpQrcUAwAgIaH8FIFbigGAEDDQ3ipAjcUAwCg4SG8VIEbigEA0PAQXm6CG4oBANCwcGPGauCGYgAANBwceakmbigGAEDDQHgBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFGcGl7Onz+vp59+Wn5+fmrRooXGjBmjS5cuVblOTEyMLBaL3ePZZ591ZpsAAMAgHs7c+NNPP62cnBylp6fr2rVrSkxM1Lhx4/TOO+9Uud7YsWM1b94827Kvr68z2wQAAAZxWng5dOiQNm3apK+++kp9+vSRJL3++ut6+OGH9eqrr6pNmzYO1/X19VVwcLCzWgMAAAZz2mmjjIwMtWjRwhZcJCk2NlZubm7atWtXleuuXr1arVu3Vrdu3TR9+nRduXLFYW1RUZEKCwvtHgAAoPFy2pGX3Nxc3XHHHfbfzMNDAQEBys3NdbjeU089pfbt26tNmzb65z//qalTp+rIkSN6//33K62fP3++5s6dW6e9AwCAhqvG4WXatGn6/e9/X2XNoUOHat3QuHHjbF9HRkYqJCREDz74oI4fP64777yzQv306dOVkpJiWy4sLFRoaGitvz8AAGjYahxepkyZotGjR1dZ06FDBwUHB+vs2bN24yUlJTp//nyNrmfp27evJOnYsWOVhhdvb295e3tXe3sAAMBsNQ4vgYGBCgwMvGlddHS0Lly4oD179qh3796SpM8++0xlZWW2QFIdWVlZkqSQkJCatgoAABohp12w27VrVw0aNEhjx45VZmamduzYoeTkZD355JO2dxr961//UpcuXZSZmSlJOn78uF566SXt2bNHJ0+e1IYNG5SQkKD7779f3bt3d1arAADAIE79kLrVq1erS5cuevDBB/Xwww9rwIAB+tOf/mR7/tq1azpy5Ijt3UReXl769NNP9dBDD6lLly6aMmWKHn/8cX344YfObBMAABjEqR9SFxAQUOUH0oWFhclqtdqWQ0NDtW3bNme2BAAADMe9jQAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABjFw9UNAPXF18tDJ18Z4uo2AAC3iCMvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKM4Lbz89re/Vb9+/eTr66sWLVpUax2r1apZs2YpJCRETZo0UWxsrI4ePeqsFgEAgIGcFl6Ki4s1bNgwJSUlVXudP/zhD3rttde0fPly7dq1S02bNlVcXJyuXr3qrDYBAIBhPJy14blz50qS0tLSqlVvtVq1ZMkSzZw5U/Hx8ZKkv/zlLwoKCtK6dev05JNPOqtVAABgEKeFl5o6ceKEcnNzFRsbaxvz9/dX3759lZGR4TC8FBUVqaioyLZcWFjo9F4BALgd+Xp56OQrQ1zdRsMJL7m5uZKkoKAgu/GgoCDbc5WZP3++7SgPgMaroUyaAFyvRte8TJs2TRaLpcrH4cOHndVrpaZPn66CggLb4/Tp0/X6/QEAQP2q0ZGXKVOmaPTo0VXWdOjQoVaNBAcHS5Ly8vIUEhJiG8/Ly1PPnj0druft7S1vb+9afU8AAGCeGoWXwMBABQYGOqWR8PBwBQcHa8uWLbawUlhYqF27dtXoHUsAAKBxc9pbpbOzs5WVlaXs7GyVlpYqKytLWVlZunTpkq2mS5cu+uCDDyRJFotFkydP1ssvv6wNGzZo//79SkhIUJs2bTR06FBntQkAAAzjtAt2Z82apZUrV9qWe/XqJUn6/PPPFRMTI0k6cuSICgoKbDUvvPCCLl++rHHjxunChQsaMGCANm3aJB8fH2e1CQAADOO08JKWlnbTz3ixWq12yxaLRfPmzdO8efOc1RYAADAc9zYCAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABilwdxVGgDQuHFncNQVjrwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCjc26iauCcHAAANA0deAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEbxcHUDdc1qtUqSCgsLXdwJAACorvLf2+W/x6vS6MLLxYsXJUmhoaEu7gQAANTUxYsX5e/vX2WNxVqdiGOQsrIynTlzRs2bN5fFYqnTbRcWFio0NFSnT5+Wn59fnW4b9YN9aDb2n/nYh+Zz1j60Wq26ePGi2rRpIze3qq9qaXRHXtzc3NSuXTunfg8/Pz9edIZjH5qN/Wc+9qH5nLEPb3bEpRwX7AIAAKMQXgAAgFEILzXg7e2t2bNny9vb29WtoJbYh2Zj/5mPfWi+hrAPG90FuwAAoHHjyAsAADAK4QUAABiF8AIAAIxCeAEAAEYhvKDRi4mJ0eTJk13dBtAojR49WkOHDnV1G7jNNLpP2AUA1J/U1NRq3UgPZoiJiVHPnj21ZMkSV7dSJcILAKDWqvtx7kBd4rRRNf3tb39TZGSkmjRpolatWik2NlaXL192dVuoppKSEiUnJ8vf31+tW7fWiy++yF+LDUxMTIwmTpyoyZMnq2XLlgoKCtKKFSt0+fJlJSYmqnnz5urYsaM+/vhjSVJaWppatGhht41169bV+Q1ZcZ2jOfDHp40uXryop59+Wk2bNlVISIgWL15c4dRtWFiYXn75ZSUkJKhZs2Zq3769NmzYoPz8fMXHx6tZs2bq3r27du/ebVvn3//+t4YPH662bdvK19dXkZGRWrNmTT3+CzR+o0eP1rZt25SamiqLxSKLxaLjx49rzJgxCg8PV5MmTdS5c2elpqbarVfZqfmhQ4dq9OjRTuuV8FINOTk5Gj58uH71q1/p0KFD2rp1qx577DF++Rlk5cqV8vDwUGZmplJTU7Vo0SK99dZbrm4LP7Jy5Uq1bt1amZmZmjhxopKSkjRs2DD169dPe/fu1UMPPaSRI0fqypUrrm71tlKTOTAlJUU7duzQhg0blJ6eru3bt2vv3r0V6hYvXqz+/ftr3759GjJkiEaOHKmEhASNGDFCe/fu1Z133qmEhATb97h69ap69+6tjRs36sCBAxo3bpxGjhypzMxMp//8t4vU1FRFR0dr7NixysnJUU5Ojtq1a6d27dpp7dq1OnjwoGbNmqUZM2bovffec22zVtzUnj17rJKsJ0+edHUrqIWBAwdau3btai0rK7ONTZ061dq1a1cXdoUfGzhwoHXAgAG25ZKSEmvTpk2tI0eOtI3l5ORYJVkzMjKsf/7zn63+/v522/jggw+sTGt1r6o5cNSoUdb4+Hir1Wq1FhYWWj09Pa1r1661PX/hwgWrr6+vddKkSbax9u3bW0eMGGFbLt+vL774om0sIyPDKsmak5PjsK8hQ4ZYp0yZcgs/GX5s4MCBdvuqMhMmTLA+/vjjVa4THx9vHTVqVN03+H848lINPXr00IMPPqjIyEgNGzZMK1as0Pfff+/qtlADP/nJT+xOJ0RHR+vo0aMqLS11YVf4se7du9u+dnd3V6tWrRQZGWkbCwoKkiSdPXu23nu7nVV3Dvz222917do1RUVF2cb8/f3VuXPnCrU37uvy/VrVvi4tLdVLL72kyMhIBQQEqFmzZtq8ebOys7Pr5oeEQ0uXLlXv3r0VGBioZs2a6U9/+pPL/90JL9Xg7u6u9PR0ffzxx4qIiNDrr7+uzp0768SJE65uDWhUPD097ZYtFovdWHkALSsrk5ubW4XTFteuXXN+k7chZ8yBle1XR/takhYsWKDU1FRNnTpVn3/+ubKyshQXF6fi4uJa94Cb++tf/6rnn39eY8aM0SeffKKsrCwlJiba/bu74rVIeKkmi8Wi/v37a+7cudq3b5+8vLz0wQcfuLotVNOuXbvslr/88kt16tRJ7u7uLuoItyowMFAXL160u3A+KyvLdQ01ctWZAzt06CBPT0999dVXtrGCggJ98803t/z9d+zYofj4eI0YMUI9evRQhw4d6mS7sOfl5WV3RHrHjh3q16+fxo8fr169eqljx446fvy43TqBgYHKycmxLZeWlurAgQNO7ZPwUg27du3S7373O+3evVvZ2dl6//33lZ+fr65du7q6NVRTdna2UlJSdOTIEa1Zs0avv/66Jk2a5Oq2cAv69u0rX19fzZgxQ8ePH9c777yjtLQ0V7fVKFV3DmzevLlGjRql3/zmN/r888/19ddfa8yYMXJzc7vld4F16tRJ6enp2rlzpw4dOqRf//rXysvLu6VtoqKwsDDt2rVLJ0+e1Llz59SpUyft3r1bmzdv1jfffKMXX3zRLpxK0k9/+lNt3LhRGzdu1OHDh5WUlKQLFy44tU/CSzX4+fnpiy++0MMPP6y77rpLM2fO1MKFCzV48GBXt4ZqSkhI0A8//KCoqChNmDBBkyZN0rhx41zdFm5BQECAVq1apY8++sj2ttk5c+a4uq1GqSZz4KJFixQdHa2f/exnio2NVf/+/dW1a1f5+PjcUg8zZ87UPffco7i4OMXExCg4OJhP9nWC559/Xu7u7oqIiFBgYKDi4uL02GOP6YknnlDfvn3173//W+PHj7db51e/+pVGjRqlhIQEDRw4UB06dNADDzzg1D4t1h+fqAIAoI5cvnxZbdu21cKFCzVmzBhXt4NGgk/YBQDUmX379unw4cOKiopSQUGB5s2bJ0mKj493cWdoTAgvAIA69eqrr+rIkSPy8vJS7969tX37drVu3drVbaER4bQRAAAwChfsAgAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABG+f+OlMvxhxr3NwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGzCAYAAADnmPfhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAoGElEQVR4nO3de1iVdb7//9fiLCooSRyULZiWkngOB7WBJnZo5uCu7ZSlqGO6R6OfRk2puzzVHitPUaPZ1NXg/KzGbOehbWlm6ThKkgd2mofMVCwXiKOChwSB9f3DzRpXsHCBLBYffD6ua11xf9b7vtcb79bN6z6se1lsNptNAAAAhvDydAMAAAC1QXgBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAHgkpkzZ8pisejUqVM11kVHR2v06NEN0xSAGxLhBYDxPv30U40dO1Zdu3aVt7e3oqOjndZWVFTo5ZdfVkxMjAICAtStWze999571dbu379fAwcOVIsWLRQSEqKRI0eqsLDQTb8FAFf5eLoBAE3LwYMH5eXVsPtF7777rpYvX65evXopMjKyxtr//M//1Isvvqhx48bpjjvu0OrVq/Xwww/LYrHooYcestf98MMP+uUvf6ng4GD94Q9/0Pnz5zVv3jzt2bNHOTk58vPzc/evBcAJC1/MCMAVM2fO1KxZs1RYWKg2bdp4uh0HJ06cUGhoqHx9fXXfffdp7969Onr0aJW6H3/8UTExMRo/frz++Mc/SpJsNpsSExN15MgRHT16VN7e3pKkiRMnKisrSwcOHNC//Mu/SJI+++wz/eu//qveeOMNjR8/vsF+PwCOOG0EoFZOnTql3/zmNwoKCtJNN92kSZMm6dKlS/bnf37NS1ZWliwWi7Zu3aqMjAyFhoaqefPm+rd/+7d6OwUTGRkpX1/fa9atXr1aly9f1sSJE+1jFotFEyZM0A8//KDs7Gz7+H//93/rvvvuswcXSUpOTtatt96q999/v176BlA3nDYCUCu/+c1vFB0drTlz5ujLL7/Uq6++qjNnzugvf/lLjfM9/vjjat26tWbMmKGjR4/qlVdeUXp6upYvX26vOX/+vEMQcsbX11fBwcG17n337t1q3ry5unTp4jAeHx9vf37AgAH68ccfdfLkSfXp06fKMuLj4/Xxxx/X+rUB1B/CC4BaiYmJ0erVqyVJjz32mIKCgrR48WI99dRT6tatm9P5brrpJn366aeyWCySrlw4++qrr6qoqMgeRNLT07V06dJr9pCYmKhNmzbVuner1aqwsDB7D5UiIiIkXTn9VFl39fjPa0+fPq2SkhL5+/vXugcA14/wAqBWHnvsMYfpxx9/XIsXL9bHH39cY3gZP368Q2i48847tXDhQh07dsw+39NPP60RI0Zcs4fWrVvXqfeffvqp2sAREBBgf/7q/16rlvACeAbhBUCtdOrUyWH6lltukZeXV7UXyF7t6mtHpH8GkDNnztjHYmNjFRsbWz+NVqNZs2YqKSmpMl55qqpZs2YO/3WlFkDDI7wAuC4/PwXjTOWneH7u6g88FhUV2Y961MTPz08hISGuNXiViIgIffHFF7LZbA59V54mqvyYdeXposrxq1mtVoWEhHDUBfAgPm0EoFYOHTrkMP3dd9+poqKixhvDuWrSpEmKiIi45uP++++v0/J79Oihixcvav/+/Q7j27dvtz8vSW3btlVoaKh27NhRZRk5OTn2OgCewZEXALWyaNEi3XPPPfbp1157TZI0aNCg6162u695SU1N1RNPPKHFixc73OdlyZIlatu2rfr162evfeCBB7R06VIdP35cUVFRkqSNGzfq22+/1RNPPFGn1wdQPwgvAGrlyJEj+vWvf62BAwcqOztby5Yt08MPP6zu3btf97Lres3L119/rTVr1ki6ciSoqKhIL7zwgiSpe/fuGjJkiCSpXbt2mjx5subOnavLly/rjjvu0KpVq7Rlyxa98847Dqe2pk2bphUrVuiuu+7SpEmTdP78ec2dO1dxcXEaM2bMdf+uAOqO8AKgVpYvX67p06drypQp8vHxUXp6uubOnevRnnbt2qXnnnvOYaxyetSoUfbwIkkvvviiWrdurTfeeENZWVnq1KmTPYBdLSoqSps3b1ZGRoamTJkiPz8/DR48WPPnz+d6F8DD+HoAAABgFC7YBQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwSpO7z0tFRYVOnDihli1buvydKwAAwLNsNpvOnTunyMhIeXnVfGylyYWXEydO2G/lDQAAzHL8+HG1a9euxpomF15atmwp6covHxQU5OFuAACAK4qLixUVFWX/O16TJhdeKk8VBQUFEV4AADCMK5d8cMEu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4eUauK01AACNC18P4AJuaw0AQOPBkRcXcVtrAAAaB8ILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABiF8AIAAIxCeAEAAEYhvAAAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARiG8AAAAoxBeAACAUQgvAADAKIQXAABgFMILAAAwCuEFAAAYhfACAACMQngBAABGIbwAAACjEF4AAIBRCC8AAMAohBcAAGAUwgsAADAK4QUAABjFreHl9OnTeuSRRxQUFKRWrVpp7NixOn/+fI3zJCUlyWKxODx+97vfubNNAABgEB93LvyRRx6R1WrVhg0bdPnyZY0ZM0bjx4/Xu+++W+N848aN0+zZs+3TgYGB7mwTAAAYxG3hZf/+/Vq3bp2++uor9enTR5L02muv6d5779W8efMUGRnpdN7AwECFh4e7qzUAAGAwt502ys7OVqtWrezBRZKSk5Pl5eWl7du31zjvO++8ozZt2qhr166aOnWqLl686LS2pKRExcXFDg8AANB0ue3IS35+vm6++WbHF/PxUUhIiPLz853O9/DDD6t9+/aKjIzU119/rWeeeUYHDx7Uhx9+WG39nDlzNGvWrHrtHQAANF61Di9TpkzRSy+9VGPN/v3769zQ+PHj7T/HxcUpIiJCd999tw4fPqxbbrmlSv3UqVOVkZFhny4uLlZUVFSdXx8AADRutQ4vTz75pEaPHl1jTYcOHRQeHq6TJ086jJeVlen06dO1up6lb9++kqTvvvuu2vDi7+8vf39/l5cHAADMVuvwEhoaqtDQ0GvWJSQk6OzZs9q5c6d69+4tSfr8889VUVFhDySuyM3NlSRFRETUtlUAANAEue2C3S5dumjgwIEaN26ccnJytHXrVqWnp+uhhx6yf9Loxx9/VOfOnZWTkyNJOnz4sJ5//nnt3LlTR48e1Zo1a5SWlqZf/vKX6tatm7taBQAABnHrTereeecdde7cWXfffbfuvfdeDRgwQH/605/sz1++fFkHDx60f5rIz89Pn332me655x517txZTz75pB544AF99NFH7mwTAAAYxK03qQsJCanxhnTR0dGy2Wz26aioKG3evNmdLQEAAMPx3UYAAMAohBcAAGAUwgsAADAK4QUAABiF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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADCKU+Pl3Llzeuihh+Tn56c2bdpo8uTJunjxYo3bxMbGymKx2D0effRRZ04TAAAYxMOZO3/ooYeUm5urLVu26MqVK5o0aZKmTp2qt956q8btpkyZomeffda27Ovr68xpAgAAgzgtXg4fPqxNmzbpyy+/1IABAyRJr7zyiu6++2699NJL6tChg8NtfX19FRwc7KypAQAAgzntslF6erratGljCxdJiouLk5ubmzIyMmrcdu3atQoICFCPHj00Z84cFRcXOxxbUlKioqIiuwcAAGi6nHbmJS8vT7fccov9F/PwULt27ZSXl+dwuwcffFCdO3dWhw4d9M9//lNPPfWUjh49qvfff7/a8SkpKVq4cGG9zh0AADReNxwvs2fP1p///Ocaxxw+fPimJzR16lTbn3v27KmQkBDdddddOn78uG699dYq4+fMmaPk5GTbclFRkUJDQ2/66wMAgMbthuPl8ccf18SJE2sc06VLFwUHB+vMmTN268vKynTu3Lkbup9l0KBBkqRvvvmm2njx9vaWt7d3rfcHAADMdsPxEhgYqMDAwOuOi46O1vnz57Vv3z71799fkvT555+roqLCFiS1kZWVJUkKCQm50akCAIAmyGk37Hbv3l3Dhw/XlClTlJmZqV27dikpKUkPPPCA7ZNG3333nbp166bMzExJ0vHjx/Xcc89p3759OnHihD788EMlJibq17/+tXr16uWsqQIAAIM49YfUrV27Vt26ddNdd92lu+++W0OHDtWrr75qe/7KlSs6evSo7dNEXl5e+uyzzzRs2DB169ZNjz/+uH7729/qo48+cuY0AQCAQZz6Q+ratWtX4w+kCwsLk9VqtS2HhoZqx44dzpwSAAAwHL/bCAAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAA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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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AOrPggULnLqRHsyQkJCg7t27a/78+e5upVqEFwBArTl7OXegLjFs5KT33ntPXbp0UaNGjdSyZUslJiaqqKjI3W3BSaWlpUpNTVVwcLBatWqlxx57jP8WPUxCQoLGjx+viRMnqnnz5goNDdWrr76qoqIijRgxQk2bNlX79u318ccfS5IyMjLUrFkzu3WsWLGizm/IinMc7QMvHDY6efKk7rnnHjVu3Fjh4eGaN29epaHbqKgoPfHEE0pJSVGTJk3Utm1brVq1SkePHlVycrKaNGmirl27asuWLbZlfvvtNw0ZMkStW7dWYGCgunTporfffrsefwIN3/Dhw7Vx40YtWLBAFotFFotFe/fu1ciRIxUdHa1GjRrp6quv1oIFC+yWq2poftCgQRo+fLjLeiW8OCE3N1dDhgzRfffdp507d2rDhg267bbb+ONnkCVLlsjHx0fZ2dlasGCBnnvuOb322mvubgsXWLJkiVq1aqXs7GyNHz9eY8aM0eDBg9WnTx9t27ZNN910k+69916dPn3a3a1eVmqyD0xLS9OmTZu0atUqrVu3Tl9++aW2bdtWqW7evHnq27evtm/froEDB+ree+9VSkqKhg4dqm3btunKK69USkqK7TXOnDmjXr16afXq1dqxY4dGjx6te++9V9nZ2S7//i8XCxYsUFxcnEaNGqXc3Fzl5uaqTZs2atOmjd599119//33mj59uh599FG988477m3WiovaunWrVZJ1//797m4FtRAfH2/t1KmTtby83DbvkUcesXbq1MmNXeFC8fHx1n79+tmmS0tLrY0bN7bee++9tnm5ublWSdbMzEzrG2+8YQ0ODrZbxwcffGBlt1b3qtsHDhs2zJqcnGy1Wq3WwsJCq6+vr/Xdd9+1PX/ixAlrYGCgdcKECbZ5bdu2tQ4dOtQ2XbFdH3vsMdu8zMxMqyRrbm6uw74GDhxonTRp0iV8Z7hQfHy83baqyrhx46y33357tcskJydbhw0bVvcN/hdHXpzQrVs33XjjjerSpYsGDx6sV199VcePH3d3W6iB6667zm44IS4uTnv27FFZWZkbu8KFunbtavva29tbLVu2VJcuXWzzQkNDJUlHjhyp994uZ87uA3/66SeVlJSod+/etnnBwcG6+uqrK9Wev60rtmt127qsrEyPP/64unTpohYtWqhJkyb65JNPdPDgwbr5JuHQwoUL1atXL4WEhKhJkyZ65ZVX3P5zJ7w4wdvbW+vWrdPHH3+smJgYvfDCC7r66qu1b98+d7cGNCi+vr520xaLxW5eRQAtLy+Xl5dXpWGLkpIS1zd5GXLFPrCq7epoW0vSnDlztGDBAj3yyCP6/PPPlZOTo6SkJBUXF9e6B1zcP//5Tz300EMaOXKk1q5dq5ycHI0YMcLu5+6O9yLhxUkWi0V9+/bVrFmztH37dvn5+emDDz5wd1twUlZWlt30V199pQ4dOsjb29tNHeFShYSE6OTJk3Ynzufk5LivoQbOmX1gu3bt5Ovrq6+//to2r6CgQD/88MMlv/6mTZuUnJysoUOHqlu3bmrXrl2drBf2/Pz87I5Ib9q0SX369NHYsWPVo0cPtW/fXnv37rVbJiQkRLm5ubbpsrIy7dixw6V9El6ckJWVpSeffFJbtmzRwYMH9f777+vo0aPq1KmTu1uDkw4ePKi0tDTt3r1bb7/9tl544QVNmDDB3W3hEsTGxiowMFCPPvqo9u7dq2XLlikjI8PdbTVIzu4DmzZtqmHDhunhhx/W559/ru+++04jR46Ul5fXJX8KrEOHDlq3bp02b96snTt36m9/+5vy8/MvaZ2oLCoqSllZWdq/f79+/fVXdejQQVu2bNEnn3yiH374QY899phdOJWkP/3pT1q9erVWr16tXbt2acyYMTpx4oRL+yS8OCEoKEhffPGFbr75Zl111VWaNm2a5s6dqwEDBri7NTgpJSVFv//+u3r37q1x48ZpwoQJGj16tLvbwiVo0aKFli5dqo8++sj2sdmZM2e6u60GqSb7wOeee05xcXG65ZZblJiYqL59+6pTp04KCAi4pB6mTZumnj17KikpSQkJCQoLC+PKvi7w0EMPydvbWzExMQoJCVFSUpJuu+023XnnnYqNjdVvv/2msWPH2i1z3333adiwYUpJSVF8fLzatWunG264waV9WqwXDlQBAFBHioqK1Lp1a82dO1cjR450dztoILjCLgCgzmzfvl27du1S7969VVBQoNmzZ0uSkpOT3dwZGhLCCwCgTj377LPavXu3/Pz81KtXL3355Zdq1aqVu9tCA8KwEQAAMAon7AIAAKMQXgAAgFEILwAAwCiEFwAAYBTCCwAAMArhBQAAGIXwAgAAjEJ4AQAARvn/C0jN0elniLkAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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EMXFxenVV19twO9A4zd27Fht2rRJWVlZstlsstls2rt3r8aPH6+OHTuqadOm6tatm7Kysjy2q+7Q/PDhwzV27FjL5kq81EJ+fr5Gjhype++9V7t27dLGjRt122238cvPICtWrFCTJk2Uk5OjrKwsPfnkk3r++ed9PS2cZ8WKFWrbtq1ycnI0efJkTZw4USNGjNDAgQO1fft23XzzzRo9erROnTrl66leUeryGJienq7Nmzdr9erVWr9+vT755BNt3779gnGLFi3SoEGDtGPHDg0bNkyjR49WSkqKRo0ape3bt6tz585KSUlx38aZM2fUt29frVmzRl9++aVSU1M1evRo5eTkWP71XymysrIUHx+vCRMmKD8/X/n5+Wrfvr3at2+vN954Qzt37lRGRoZmz56t119/3beTdeGitm3b5pLk2r9/v6+ngkswePBgV48ePVxVVVXudQ8//LCrR48ePpwVzjd48GDX9ddf716uqKhwNWvWzDV69Gj3uvz8fJckV3Z2tuvPf/6zy+l0euxj5cqVLh7W6l9Nj4FjxoxxJScnu1wul6ukpMQVGBjoeuONN9zXHz9+3BUSEuKaMmWKe12HDh1co0aNci+fu18feeQR97rs7GyXJFd+fr7XeQ0bNsw1bdq0y/jKcL7Bgwd73FfVmTRpkuv222+vcZvk5GTXmDFj6n+C/8UzL7XQq1cv3XTTTYqLi9OIESO0bNkyHTt2zNfTQh0MGDDA43BCfHy8vvnmG1VWVvpwVjhfz5493f8OCAhQmzZtFBcX514XHh4uSTp8+HCDz+1KVtvHwO+++05nz55Vv3793OucTqe6det2wdgf3tfn7tea7uvKyko99thjiouLU+vWrdW8eXOtW7dOeXl59fNFwqslS5aob9++CgsLU/PmzfXcc8/5/PtOvNRCQECA1q9fr/fff1+xsbFavHixunXrpn379vl6akCjEhgY6LFss9k81p0L0KqqKtnt9gsOW5w9e9b6SV6BrHgMrO5+9XZfS9Ljjz+urKwsPfzww/roo4+Um5urpKQklZeXX/IccHF//etfNX36dI0fP14ffPCBcnNzNW7cOI/vuy9+FomXWrLZbBo0aJDmzZunHTt2KCgoSCtXrvT1tFBLW7Zs8Vj+7LPP1LVrVwUEBPhoRrhcYWFhOnHihMeJ87m5ub6bUCNXm8fATp06KTAwUJ9//rl7XXFxsb7++uvLvv3NmzcrOTlZo0aNUq9evdSpU6d62S88BQUFeTwjvXnzZg0cOFAPPPCA+vTpoy5dumjv3r0e24SFhSk/P9+9XFlZqS+//NLSeRIvtbBlyxb99re/1datW5WXl6e33npLRUVF6tGjh6+nhlrKy8tTenq69uzZo1dffVWLFy/WlClTfD0tXIb+/fsrJCREs2fP1t69e/XKK69o+fLlvp5Wo1Tbx8AWLVpozJgxeuihh/TRRx/pq6++0vjx42W32y/7VWBdu3bV+vXr9emnn2rXrl369a9/rcLCwsvaJy4UExOjLVu2aP/+/Tpy5Ii6du2qrVu3at26dfr666/1yCOPeMSpJP3sZz/TmjVrtGbNGu3evVsTJ07U8ePHLZ0n8VILoaGh+vjjj3XLLbfo6quv1pw5c7Rw4UINHTrU11NDLaWkpOj06dPq16+fJk2apClTpig1NdXX08JlaN26tV566SW999577pfNPvroo76eVqNUl8fAJ598UvHx8fr5z3+uxMREDRo0SD169FBwcPBlzWHOnDm69tprlZSUpISEBEVERPDOvhaYPn26AgICFBsbq7CwMCUlJem2227TnXfeqf79++s///mPHnjgAY9t7r33Xo0ZM0YpKSkaPHiwOnXqpBtvvNHSedpc5x+oAgCgnpSWlqpdu3ZauHChxo8f7+vpoJHgHXYBAPVmx44d2r17t/r166fi4mLNnz9fkpScnOzjmaExIV4AAPXqiSee0J49exQUFKS+ffvqk08+Udu2bX09LTQiHDYCAABG4YRdAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFH+P/6YTi9kzckLAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ref = None\n", "for bin, values in results.items():\n", " plt.figure()\n", " m = np.mean(values, axis=0) - truth\n", " s = np.std(values, axis=0, ddof=1)\n", " plt.title(f\"{bin=}\")\n", " plt.errorbar(np.arange(len(m)), m / s, 1, fmt=\"o\", label=f\"{bin=}\")\n", " plt.axhline(0, ls=\"--\", color=\"0.5\")\n", " plt.xticks(np.arange(len(m)), [\"s\", \"b\", \"mu\", \"sigma\", \"tau\"]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The plots show the bias relative to the standard deviation for each parameter. All results are unbiased, whatever the binning. The bias is not exactly zero, since we used only 100 repetitions, it shrinks further with more. One can observe that the residual bias that is coming from the finite sampling is the same for the unbinned fit and the fits with 100 and 200 bins, which are essentially equivalent." ] } ], "metadata": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" }, "kernelspec": { "display_name": "Python 3.8.12 ('venv': venv)", "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.10.8" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/conditional_variable.ipynb0000644000000000000000000032627714332717401020211 0ustar00{ "cells": [ { "cell_type": "markdown", "id": "naked-recruitment", "metadata": {}, "source": [ "# Fit PDF with conditional variable\n", "\n", "In this example, we show an unusual fit where the total sample is not drawn form a single probability distribution, but each individual sample $x$ is drawn from a different distribution, whose parameters are determined by a conditional variable $y$.\n", "\n", "In our example, we are drawing samples $x$ from varying Gaussian distributions. The location of each Gaussian is a function of the conditional variable $y$, but all share the same width parameter $\\sigma$. We fit the shared parameter $\\sigma$, but also the parameters $a$ and $b$ which determine how the location of each gaussian depends on $y$, assuming a line function $\\mu = a + b y$.\n", "\n", "This tutorial reproduces a [corresponding one from RooFit](https://root.cern.ch/doc/master/rf303__conditional_8C.html)." ] }, { "cell_type": "code", "execution_count": 1, "id": "technological-economy", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "iminuit version 2.19.0\n" ] } ], "source": [ "import iminuit\n", "from iminuit.cost import UnbinnedNLL\n", "from iminuit import Minuit\n", "import numpy as np\n", "import numba as nb\n", "import boost_histogram as bh\n", "import matplotlib.pyplot as plt\n", "from scipy.stats import norm\n", "from numba_stats import norm as norm_nb\n", "print(\"iminuit version\", iminuit.__version__)" ] }, { "cell_type": "code", "execution_count": 2, "id": "wicked-animal", "metadata": {}, "outputs": [], "source": [ "rng = np.random.default_rng(1)\n", "\n", "# conditional variable: each sample is paired with a random y parameter\n", "y = rng.normal(0, 10, size=10000)\n", "y = y[np.abs(y) < 10] # truncate at 10\n", "\n", "# location of each gaussian is a function of y\n", "def mu(y, a, b):\n", " return a + b * y\n", "\n", "# draw samples from Gaussians whose locations depend on y\n", "truth = {\"a\": 0, \"b\": 0.5, \"sigma\": 1.0}\n", "x = rng.normal(mu(y, truth[\"a\"], truth[\"b\"]), truth[\"sigma\"])" ] }, { "cell_type": "markdown", "id": "removable-forward", "metadata": {}, "source": [ "The distribution in $x$ is more broad than the usual Gaussian because it is a convolution of many Gaussian distributions with varying means. We can visualise this by binning the data in $x$ and $y$." ] }, { "cell_type": "code", "execution_count": 3, "id": "subjective-sleep", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ax_x = bh.axis.Regular(100, -10, 10)\n", "ax_y = bh.axis.Regular(5, -10, 10)\n", "h = bh.Histogram(ax_x, ax_y)\n", "h.fill(x, y)\n", "for i, (a, b) in enumerate(ax_y):\n", " plt.stairs(h.values()[:,i], ax_x.edges, label=f\"[{a}, {b})\",\n", " fill=True, alpha=0.2)\n", "h1 = h[:, sum]\n", "plt.stairs(h1.values(), ax_x.edges, color=\"k\", label=\"total\")\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"events\")\n", "plt.legend(title=\"y interval\", frameon=False, handlelength=1.2);" ] }, { "cell_type": "markdown", "id": "copyrighted-plenty", "metadata": {}, "source": [ "## Fit with conditional variable\n", "\n", "The random distribution of $x$ depends on the value of $y$. We can exploit that information in the likelihood function to obtain a more accurate estimate of the parameters." ] }, { "cell_type": "code", "execution_count": 4, "id": "aware-fantasy", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 1.93e+04 Nfcn = 130
EDM = 2.25e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 a -0.010 0.012
1 b 0.4993 0.0022
2 sigma 0.986 0.008 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
a b sigma
a 0.000142 1.31e-08 5.73e-08
b 1.31e-08 4.76e-06 1.02e-08
sigma 5.73e-08 1.02e-08 7.08e-05
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 1.93e+04 │ Nfcn = 130 │\n", "│ EDM = 2.25e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ a │ -0.010 │ 0.012 │ │ │ │ │ │\n", "│ 1 │ b │ 0.4993 │ 0.0022 │ │ │ │ │ │\n", "│ 2 │ sigma │ 0.986 │ 0.008 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬────────────────────────────┐\n", "│ │ a b sigma │\n", "├───────┼────────────────────────────┤\n", "│ a │ 0.000142 1.31e-08 5.73e-08 │\n", "│ b │ 1.31e-08 4.76e-06 1.02e-08 │\n", "│ sigma │ 5.73e-08 1.02e-08 7.08e-05 │\n", "└───────┴────────────────────────────┘" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def model(xy, a, b, sigma):\n", " x, y = xy\n", " mu = a + b * y\n", " # cannot use norm.pdf from numba_stats here, because it is not vectorized in mu\n", " return norm.pdf(x, mu, sigma)\n", "\n", "nll = UnbinnedNLL((x, y), model)\n", "\n", "m = Minuit(nll, 0.0, 0.0, 2.0)\n", "m.limits[\"sigma\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 5, "id": "aquatic-belgium", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAG0CAYAAADU2ObLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABf7klEQVR4nO3de1iUZf4/8PfDIKAwgIAwwKBYbZabmluJJwyTxbRcbaTdTNcsyw6e0LKyX2VaqWW7Ya7Z7n5brcx1kybJoqNHVDQPuWaWpYsJo+CR0yQgM8/vD5zHOTMzzHner+ua6+I5zdyPI8Nn7vtzf25BFEURREREREEqzNcNICIiIvIkBjtEREQU1BjsEBERUVBjsENERERBjcEOERERBTUGO0RERBTUGOwQERFRUGOwQ0REREGNwQ4REREFNQY7REREFNR8GuwsWrQIt9xyC+RyOZKTkzFmzBgcOXLE5JzGxkZMnToViYmJiImJwdixY1FdXW1yzokTJ3DHHXegU6dOSE5Oxpw5c9DS0uLNWyEiIiI/Fe7LF9+6dSumTp2KW265BS0tLXjmmWeQl5eHw4cPIzo6GgAwa9YsfPrpp1i3bh3i4uIwbdo0qFQq7NixAwCg0+lwxx13QKFQYOfOnTh16hQmTpyIDh06YOHChQ61Q6/X4+TJk5DL5RAEwWP3S0RERO4jiiLq6+uRlpaGsDA7/TeiHzl9+rQIQNy6dasoiqJYU1MjdujQQVy3bp10zg8//CACEMvKykRRFMWSkhIxLCxMrKqqks5ZsWKFGBsbKzY1NTn0uhUVFSIAPvjggw8++OAjAB8VFRV2/877tGfHXG1tLQAgISEBALBv3z5cunQJubm50jnXXXcdunbtirKyMvTv3x9lZWXo1asXUlJSpHOGDx+ORx99FN9//z369u1r8TpNTU1oamqStsXLC79XVFQgNjbWI/dGRERE7lVXV4eMjAzI5XK75/lNsKPX61FQUIBBgwbhhhtuAABUVVUhIiIC8fHxJuempKSgqqpKOsc40DEcNxyzZtGiRZg/f77F/tjYWAY7REREAaatFBS/mY01depUHDp0CGvXrvX4a82dOxe1tbXSo6KiwuOvSURERL7hFz0706ZNwyeffIJt27ZBqVRK+xUKBZqbm1FTU2PSu1NdXQ2FQiGd880335g8n2G2luEcc5GRkYiMjHTzXRAREZE/8mnPjiiKmDZtGj766CNs2rQJ3bt3Nzl+0003oUOHDti4caO078iRIzhx4gQGDBgAABgwYAC+++47nD59Wjrnq6++QmxsLHr27OmdGyEiIiK/5dOenalTp2LNmjUoLi6GXC6Xcmzi4uLQsWNHxMXFYfLkyZg9ezYSEhIQGxuL6dOnY8CAAejfvz8AIC8vDz179sSf//xnvPrqq6iqqsKzzz6LqVOnsveGiIiIIIiGqUi+eHEbCUUrV67EpEmTALQWFXz88cfx73//G01NTRg+fDjefPNNkyGqX375BY8++ii2bNmC6Oho3HfffVi8eDHCwx2L5erq6hAXF4fa2lomKBMREQUIR/9++zTY8RcMdoiIiAKPo3+//WY2FhEREZEnMNghIiKioMZgh4iIiIIagx0iIiIKagx2iIiIyC1ycnJQUFDg8PmrVq2yWBLKExjsEBERUVBjsENERERBjcEOERFRkMvJycH06dNRUFCAzp07IyUlBf/85z+h1Wpx//33Qy6X45prrsFnn30mXbN161b069cPkZGRSE1NxdNPP42WlhbpuFarxcSJExETE4PU1FT85S9/sXjdpqYmPPHEE0hPT0d0dDSysrKwZcsWb9yyCQY7REREIeCdd95BUlISvvnmG0yfPh2PPvoo7r77bgwcOBD79+9HXl4e/vznP+PXX3+FRqPByJEjccstt+C///0vVqxYgbfffhsvvfSS9Hxz5szB1q1bUVxcjC+//BJbtmzB/v37TV5z2rRpKCsrw9q1a3Hw4EHcfffduP322/Hzzz979d5ZQRmsoEwUTLRaLWJiYgAADQ0NiI6O9nGLKNiNWrYdZ+qbvP66XeSR2DB9sEPn5uTkQKfTobS0FACg0+kQFxcHlUqFd999FwBQVVWF1NRUlJWVYcOGDfjwww/xww8/SEs7vfnmm3jqqadQW1uLX3/9FYmJiVi9ejXuvvtuAMD58+ehVCoxZcoUFBYW4sSJE7jqqqtw4sQJpKWlSW3Jzc1Fv379sHDhQqxatQoFBQWoqalx6d/A0b/fPl0IlIiIKNCdqW9CVV2jr5vRpt69e0s/y2QyJCYmolevXtK+lJQUAMDp06fxww8/YMCAASZrWA4aNAgNDQ2orKzEhQsX0NzcjKysLOl4QkICevToIW1/99130Ol0uPbaa03a0dTUhMTERLffnz0MdoiIiNqhizwyIF63Q4cOJtuCIJjsMwQ2er2+/Y1Da8+qTCbDvn37IJPJTI4Zel+9hcEOERFROzg6lBRIrr/+enz44YcQRVEKgnbs2AG5XA6lUomEhAR06NABu3fvRteuXQEAFy5cwE8//YRbb70VANC3b1/odDqcPn0a2dnZPrsXgAnKREREZOaxxx5DRUUFpk+fjh9//BHFxcWYN28eZs+ejbCwMMTExGDy5MmYM2cONm3ahEOHDmHSpEkIC7sSVlx77bUYP348Jk6cCLVajfLycnzzzTdYtGgRPv30U6/eD3t2iIiIyER6ejpKSkowZ84c9OnTBwkJCZg8eTKeffZZ6ZwlS5agoaEBo0aNglwux+OPP47a2lqT51m5ciVeeuklPP7449BoNEhKSkL//v1x5513evV+OBsLnI1FFEw4G4sodDj695vDWERERBTUGOwQERFRUGOwQ0REREGNwQ4REREFNQY7REREFNQY7BAREVFQY7BDREREQY3BDhEREQU1BjtEREReptVqIQgCBEGAVqv1dXOCHoMdIiKiEJWTk4OCggJfN8PjGOwQERF5mU6nk37etm2byba/2rJlCwRBQE1Nja+b4jQGO0RERF6kVqvRs2dPaXvkyJHIzMyEWq32YauCG4MdImoXf8s9cPUbs7/dBwUntVqN/Px8aDQak/0ajQb5+fkeDXi0Wi0mTpyImJgYpKam4i9/+YvJ8ffeew8333wz5HI5FAoF7r33Xpw+fRoAcPz4cQwdOhQA0LlzZwiCgEmTJgEAPv/8cwwePBjx8fFITEzEnXfeiWPHjnnsPlzBYIeIgga/MZM/0+l0mDlzJkRRtDhm2FdQUOCxIa05c+Zg69atKC4uxpdffoktW7Zg//790vFLly7hxRdfxH//+1+sX78ex48flwKajIwMfPjhhwCAI0eO4NSpU1i6dCmA1iBq9uzZ2Lt3LzZu3IiwsDDcdddd0Ov1HrkPl4gk1tbWigDE2tpaXzeFKOA0NDSIAEQAYkNDg8/a8eGHH4qCIEhtMTwEQRAFQRA//PBDu9f7y31Q8Nq8ebPF/09rj82bN7v9tevr68WIiAjxgw8+kPadO3dO7Nixozhz5kyr1+zZs0cEINbX15u0/8KFC3Zf68yZMyIA8bvvvnNX821y9O83e3aIKOD5+hszkSNOnTrl1vOccezYMTQ3NyMrK0val5CQgB49ekjb+/btw6hRo9C1a1fI5XLceuutAIATJ07Yfe6ff/4Z48aNw1VXXYXY2FhkZmY6dJ03MdghIr/QnpyZ0tJSVFZW2jwuiiIqKipQWlra3mYSuSw1NdWt57mTVqvF8OHDERsbi/fffx979uzBRx99BABobm62e+2oUaNw/vx5/POf/8Tu3buxe/duh67zJgY7RBTwfPmNmchR2dnZUCqVEATB6nFBEJCRkYHs7Gy3v/bVV1+NDh06SIEIAFy4cAE//fQTAODHH3/EuXPnsHjxYmRnZ+O6666TkpMNIiIiAJhOAjh37hyOHDmCZ599FsOGDcP111+PCxcuuL397cVgh4gCnj9/YyYykMlkUlKvecBj2C4sLIRMJnP7a8fExGDy5MmYM2cONm3ahEOHDmHSpEkIC2sNA7p27YqIiAgsW7YM//vf//Dxxx/jxRdfNHmObt26QRAEfPLJJzhz5gwaGhrQuXNnJCYm4h//+AeOHj2KTZs2Yfbs2W5vf3v5NNjZtm0bRo0ahbS0NAiCgPXr15scN3Rpmz+WLFkinZOZmWlxfPHixV6+EyLyJV9+YyZyhkqlQlFREdLS0kz2K5VKFBUVQaVSeey1lyxZguzsbIwaNQq5ubkYPHgwbrrpJgBAly5dsGrVKqxbtw49e/bE4sWL8dprr5lcn56ejvnz5+Ppp59GSkoKpk2bhrCwMKxduxb79u3DDTfcgFmzZpn8jfYXgmgto89LPvvsM+zYsQM33XQTVCoVPvroI4wZM0Y6XlVVZXH+5MmTcfToUVx11VUAWoOdyZMn46GHHpLOk8vliI6OdrgddXV1iIuLQ21tLWJjY9t3U0QhRqvVIiYmBgDQ0NDg1O+eO5/HUL8EgEmisiEAausPibvug8gRhr87AFBSUoK8vDyP9OgEO0f/fod7sU0WRowYgREjRtg8rlAoTLaLi4sxdOhQKdAxMBRAIqLQZfjGPGPGDJOCbUqlEoWFhR79xkzkLOPAZsiQIQx0PCxgcnaqq6vx6aefYvLkyRbHFi9ejMTERPTt2xdLlixBS0uL3edqampCXV2dyYOIAp9KpcLhw4el7ZKSEpSXlzPQIb8THR0NURQhiiJ7Eb3Apz07znjnnXcgl8stPrRmzJiB3/3ud0hISMDOnTsxd+5cnDp1Cn/9619tPteiRYswf/58TzeZiHyA35iJyFzABDv/+te/MH78eERFRZnsN8767t27NyIiIvDwww9j0aJFiIyMtPpcc+fONbmurq4OGRkZnmk4ERER+VRABDulpaU4cuQI/vOf/7R5blZWFlpaWnD8+HGTypDGIiMjbQZCREREFFwCImfn7bffxk033YQ+ffq0ee6BAwcQFhaG5ORkL7SMiIiI/J1Pe3YaGhpw9OhRabu8vBwHDhxAQkICunbtCqB1iGndunUWS9EDQFlZGXbv3o2hQ4dCLpejrKwMs2bNwoQJE9C5c2ev3QcRkS2c0k7kez4Ndvbu3YuhQ4dK24Y8mvvuuw+rVq0CAKxduxaiKGLcuHEW10dGRmLt2rV44YUX0NTUhO7du2PWrFl+Wb2RiIiIfMOnRQX9BYsKErnOX4oKtvd5PNUDw54dIs9x9O93QOTsEBEREbmKwQ4RtYvxCsjbtm0z2Q4kwXIfRGSJwQ4RuUytVqNnz57S9siRI5GZmQm1Wu3DVjkvWO6DiKxjsENELjEsvGm8DhUAaDQa5OfnB0ygECz3QUS2MdghIqfpdDrMnDkT1uY3GPYVFBQ4NRTki2EkT9wHEfkfBjtE5LTS0lJUVlbaPC6KIioqKlBaWmrzHK1WC0EQIAgC1qxZ49AwkvE1Wq3WL+6DiPwfgx0ictqpU6fcet6ECRN8Mozk7vsgIv/EYIeInJaamurW83w1jOTu+yAi/8Rgh4iclp2dDaVSCUEQrB4XBAEZGRnIzs5u1+t4ehjJW/dBRL7FYIcoRLgz30Umk2Hp0qUAYBEoGLYLCwshk8nc8trtHUay9frevg8i8g0GO0TkEpVKhaKiIqSlpZnsVyqVKCoqgkqlcttreXIYyZv3QUS+4dOFQIkosKlUKuTm5iIuLg4AUFJSgry8PKknxFGCIFjN2xEEAUql0uPDSO66DyLyT+zZIaJ2MQ4IhgwZ4nKA4MgwkjXmQ0vR0dEQRRGiKDq16KYr9+HrYS1fvz5RoGCwQ0Q+t3r1ag4jEZHHMNghIp8bPXo0Dh8+LG2XlJSgvLycgQ4RuQWDHSLyC+4aDvM3XE2dyPcY7BCR2zGXpBVXUyfyDwx2iIg8gKupE/kPBjtEIcpa7wt7ZNyDq6kT+RcGO0REbsbV1In8C4MdIiI342rqRP6FwQ4RkZtxNXUi/8Jgh4jIjbRaLYYOHQrAsiq0AVdTJ/IuBjtE5BOu1J8JxJo1ri6DQUTuw2CHiLzOWv0Z421Hr8nMzERxcbHH2tleXAaDyD9w1XMi8qri4mJMmDDBYlr2yZMnbV5jqFljfo1Go8GECRM80k53GD16NO68806upk7kY+zZIQoRvhwCMn6tturPmJ/vSM0aZ16/PffuyvME6zIYRIGEwQ5RCHBl2MhdiouLTV7r7NmzbV6zY8cO6WdHatbY464lG3z5b0hE7cNhLKIgZ2sIyN6wkTtZG7JqS1VVlfRze2rR2Bv+ys/Pdzh3xtf/hkTUPuzZIQpijg4BeXJIy9lABwAUCoX0s6u1aNy1ZIM//BsSUfuwZ4coiLU1BGSwY8cOjBgxwqXXiI6OdimgsWfQoEHSz9nZ2VAqldBoNBavo5j4OmQxna9c98oWREVFAhDQ1NQE8Q8vId3Ga+gaLqDi3VkoLS1FTk6OxX1otVrExMQ43OYVK1bgiSeecPh8dzDPIWLyM5F1DHaIgpijQ0DGw0a+IgiCFGwY/8GWyWRYunQppn58ArLozibXhMuTTLZrmgE0N9k8bn5t+mOrMGNjPTrt3AgA6CKPxIbpg11q/9NPP42//e1veOWVV1y63llqtRozZsyQtkeOHAmlUomlS5dyWjuRGQY7REHM0SEg42EjX0lPTzfphRq1bDtO112E5nJeTKTiN3avb6k3TXyOjY1FXV2dxXnGAVC4PAl1l4C6S40AgKq6RvRf2Br4iKIeiomvo+rdWQ7fg7emwrsrF4koVAiiu/ufA1BdXR3i4uJQW1uL2NhYXzeHyG10Oh0yMzOtDgEZM/zfNx66aWhoQHR0tEuva/w8xj02tmS/+Amaw1qHnwyq6hptnt9SfxZhYWHQ6/UAWoekjIMSQRCQnt46gGV+78ZDXzKZDKkKBarqmmBLS/1ZyMJk0Ol1Vl/LnPH9NjQ0AIBL/6a23gvDe2preFIQBCiVSpSXl3NIi4Keo3+/2bNDFMQMQ0D5+fkWQYetYSN3MM4lEUXR4rUV971uMiR1ogEA7Acc6ZcrEZ/46fs2e1tEUURlZSXmz5+PF154weT1q96dJS3Z0NoDkotRy7bjTP2V1zcOtAw9QY5+WHr6+6MjU/ErKiqkXCQi8vFsrG3btmHUqFFIS0uDIAhYv369yfFJkyZBEASTx+23325yzvnz5zF+/HjExsYiPj4ekydPlr5NERGgUqlQVFRksWyBoefD3czr0QCW60NFxCYhXH7lYUwRGyU9UuQRaDr1MzRvTsKmgoHYVDDQqWGl3/zmN1bv3XzJhg3TB2PXM8OkR6/0OKTII9BSfxYt9WeRIo+AgMuBYXRnpD+2SnooJr7ucHvcwdE8rPZM2ScKNj7t2dFqtejTpw8eeOABm+PLt99+O1auXCltR0ZGmhwfP348Tp06ha+++gqXLl3C/fffjylTpmDNmjUebTtRIFGpVMjNzTVZtqB///5ISEgAcGUmT3tn91jLJTGfMZWU2JonoxcBUa+DTnsB6WlpEIQwiwTh1qGcPJfvOzU1FTk5ORb33tZ9bZg+2GQY6UhDA4Yt3YWqukYIYTK7ic+e5mgelqtT9omCkU+DnREjRrQ53TUyMtJm8uQPP/yAzz//HHv27MHNN98MAFi2bBlGjhyJ1157zeLbHFEoM/7jfuHCBfTq1UvaHjlyJBITE02CFFuze+zlklirRyOL6WwSHNQ0Xzmm016A5s1JOOJCflB6ejpOnjxpddjIkLeSnZ1tce+uLtnQRR4JUdRLCdOy6M4QwmRST4+xtvJ6XKXVajF06FAAtnOhzO/d0ed1R64Wkb/y+5ydLVu2IDk5GZ07d8Ztt92Gl156CYmJiQCAsrIyxMfHS4EOAOTm5iIsLAy7d+/GXXfdZfU5m5qa0NR0ZXze2owNomBmrarxuXPnLM5zZnaPIZfEvCfHkJtj6MVJSkxCVFQURFGPE6cuuHwPr776KiZMmGA1FwkACgsL3ZqLZN7bk/7YKoTLk3za0+OteycKdH5dQfn222/Hu+++i40bN+KVV17B1q1bMWLECKmrvaqqCsnJySbXhIeHIyEhwW7dkEWLFiEuLk56ZGRkePQ+iPyNo0m0zlQaNuSIGHpyDA8hrPWPrqEXZ2b309j1zDCn82/MjR492qF8HE/plhwPUXtByutpqT8L8fLsMENvz6BXtmBo4U6P5PWsXr3aZ/dOFGj8umfnnnvukX7u1asXevfujauvvhpbtmzBsGHDXH7euXPnYvbs2dJ2XV0dAx4iGxyd3WOeI2LoyTHQNbT+7M6aPtZykbxVRfjLJ3Kh0+lMXvvpnTrUXoLU29Na5LDZpKfLXUaPHo0777zTJ/dOFGj8Otgxd9VVVyEpKQlHjx7FsGHDoFAocPr0aZNzWlpacP78ebsfqJGRkRaJzkRkn7XZPX/8v704p225vCWi6/T3gKjWWheGnhxzxktBuIM78nHc8doXLlzA6YrTJlPqDXk93nh9b987wFwfChwBFexUVlbi3Llz0jfIAQMGoKamBvv27cNNN90EANi0aRP0ej2ysrJ82VSioGNtds/ZhmZU11/JOBY6We/B8GRNH3/x5JNPokqjMdlnyOsxDGsNLdyJMKE1e6A9S1MQkXN8Guw0NDTg6NGj0nZ5eTkOHDiAhIQEJCQkYP78+Rg7diwUCgWOHTuGJ598Etdccw2GDx8OALj++utx++2346GHHsJbb72FS5cuYdq0abjnnns4E4vIDkeqGhufa5jdY1jCwTD76ExDa6ATJgDJ8igAwMWLF1FTUyMNWwGWS0EEI41ZoGPMMKx12igwJCLv8Wmws3fvXmkaJQApj+a+++7DihUrcPDgQbzzzjuoqalBWloa8vLy8OKLL5oMQb3//vuYNm0ahg0bhrCwMIwdOxZvvPGG1++FKNA4EvCYz+45U9+E6vpmafaR/vLlyfIo7HrmSh6doYQ70JpLMmjQIGk7lBgHfACQkJCAhpYw6d+NiLzDp8FOTk6O3Q/bL774os3nSEhIYAFBIietXr0aTz75pElvhKHOzvnz56V9SqUShYWFFrN7rBUDNGaeSxKqzGebrd68GU/v1KGqrhGnarRQTn3HZkFFInKfgMrZISL3sDWTR6vVOjS7pz3FAIOVw0UOd25p3Xd5aKuaQ1tEHsdghyhEWZvJY7zvbz91wvxvt5hcc7re9krkwcba0hn2OFrk0LwSc0RsEvQiUKmphCAInNVE5AF+XVSQiHznbEMzquoaTR7ezjUxDzjaKmzoLuaLmY4cORKZmZkoLi62eY2jRQ43TB+MTQUDoXlzEjRvTkKXmAgAVwoR3la4E/0XbsRtHipGSBSK2LNDRHYZz7QC0O5lHhylVqsxY8YMaXvkyJEeW6nd/HXNFzMFWmdbTZgwwe617SlyaG1YyxPFCIlCEYMdIrLLfKZVe1cid4StgOPk5aEfT7G1mCnQWknaMCRlj7OF/pIu9+wYhrXS09JwpqHZpBfNkWE0IrKNwQ4RAYDNGjqeEh0dbTWocCTgUCqViIqKsjjeXobFTG1xtDaRMz54sHUhY0Ml4iMNDRi4eDNqL10JrAwr0L/yyituf32iUMBgh4gAwGYNHW9zJOBwZK0uV1hbEsPbiouLce78JZPKywAgAnj8y9N2ryUi65igTEQmRL0OLfVnkSKPgCI2yqKGjqc5GnB4IjCxtiSGtz355JPSz4Y8HsPDeN0tInIce3aIQoT5sJFWq7V6Xls1dKxNyW4rL8XWkJU1jgYczgQmjr5+dnY2lEolNBqN3Xo55eXlkMlkNv8N20Oj0UBhVnnZ2oKi27Ztc9uiqq68p0SBhD07RCFo1LLtuK1wJ9IfWyVNd7ZWQ0er1UIQBAiCAK1Wa3NKtlqtdlvbDAGHrWRgQRCQkZHRWqDPzWQyGZYuXSq9jvnrAlfq5XhS1buzpKnpmjcnQae1nP02cuRIk/fCVd54T4l8jcEOUQgyzs8xTHduK0enuLgY+fn5FgteajQa5Ofnu+2Po68DDpVK5VC9HF8w5PAYHhj9crtq8RhmvXn6PSXyNQY7RCHMPD8nRR5hsXilwZNPPmlzhhQAFBQUuK3on68DDpVKhcOHD0vbJSUlKC8v90qgk56ebrtXyyyHJ1yeJNXicfbfvq1Zb4B731MiX2KwQxQEzIebHGXIz9lUMBC7nhmGTQUDLRavNDD/9m/MeIaUuzgacBjycURRdOsyC47Uy/HEa7/66qsATHu1dA0X0FJ/1uQh6k2DkP379zv1Os7MerPFVxWuiZzFYIeI3MbdM6ScLdAXDKwtO2Gew2Mtj8fZf/v2znpjrg8FEs7GIgoBo5Ztx5n6JmnbUwt6+sPU7WBgvuyEPYY8nld/jMPShRvRRR6JDdMHt3lde2a92VtSIz8/3+e5TUTm2LNDFALO1De1e0FPu7kkZjOkPDW0FCwcGf4x7sVyJI/nQpOIqrpGk6DWHldnvTHXhwIRgx2iEBImAIrYqDaTka2xlktivO2NKdnu4mqOkztYG/5pawp5W3k8cR1EhF0+JIp6h+7N1Vlv7sj1IfI2BjtEIcSwqOeuZ4Zh9//7PU69U2DS+2KvR8ZaLgngH1OyA4W9qd4G1np6bOXxhG14Dm8Mk+O/L95psjK9o1yZ9ebLCtdErmKwQ0QO8+WU7EBnb/jHmCHRt7i42GS/p/7tnX1eT1S4JvI0JigTkVNCcYaUO7Q1/GNMo9FgwoQJFvsd+bc/09AsLR56W+FOCEJYm0nLzrynji6p4YkK10SuYrBDFGT++H97cU7bYrLPU7OvyHHODOuIomgzcbgtehHSyvXV9c1Wz9FqtYiJiQEANDQ0OPX8hlyf/Px8CIJgEvAEYv4WhQYOYxEFmbMNzSYzr1ydfUWusZX35OywjqMLpxp0kUdKieeGxOUw1+KlNvm6wjWRs9izQxSkwgRYJK12kUf6qDXU1vBPexmGqYx7bfq99KXN3p22mPf+mCesm9cCKikp4Wrp5LcY7BAFKcPMK/IP9oZ/AhXztyhQcBiLiMhLbA3/WONqzg4RWWLPDhHZZF7pNy8vz4etCQ7WloKwlejrLqfrG9F/4UZpOzGaH/0UWtizQ0RW2Vro0bz+CznPeLjn/ffft5rou3r1are9nl6EScL62QbX8niIAhXDe6IAN2rZdpyuuyjVVjnjhj9k9hZ6tFb/hVw3evRo3HnnnRaJvo2N7S8XkBQTAUG48p32dD1n5lFoYrBDFODO1Dehur5Zqq1i+GNWqamEIAhWZ9LY09ZCj6GSS2KYQu4Nnkr0/eDBm03e+/4LN6KqjjWXKPQw2CEKcBcvXgQgQNTroNO2LuwpC5M5tcinMUcWevQWbwYcRBS8GOwQBTC1Wo1z5+sRLk+CTnsBmjcnAbBMeHUGF3AkomDDYIcoQBmGm/CHlyyOGQc65itot4ULOPoHT/Zqma+fBQCKia+j6t1ZHnk9Il/jbCyiAOXowpI7duxw6nkNlX5t5eYEQ86O+ZR6ZwNCf2Z+b3V1dRAE4XJvnx7AlfWzwuVJqK5vRnV9M2QxnX3VZCKPY7BDFKAcHW6qqqpy6nkNlX4By8AmGAIdW1Pq1Wq1D1vlHtbuzXg7KSbCa+tnEfkTBjtEAcrR4SaFQuH0c9tb6NGd9V+8zTClXqPRmOzXaDTIz88P6IDH1r2dPHlS+vmDB2/GrmeGYVPBQGjenATNm5PQJSbC200l8jqfBjvbtm3DqFGjkJaWBkEQsH79eunYpUuX8NRTT6FXr16Ijo5GWloaJk6caPKLCwCZmZlSF63hsXjxYi/fCZF3jFq2Hf0XbkT/hRvx9M4WdJ3+HmTR9ocfBg0a5NJrqVQqHD58WNouKSlBeXk57r33Xqurevu7tqbUA0BBQYFfD2nZWlHdkXsznGeLMl0ZcO8pkaN8GuxotVr06dMHy5cvtzj266+/Yv/+/Xjuueewf/9+qNVqHDlyBH/4wx8szl2wYAFOnTolPaZPn+6N5hN53Zn6JqNKuE0QOnWGEGZZk8V4uKk9NVuCaaFHR6bUV1RUoLS01Iutcg9n87eMg56mJlZTpuDn09lYI0aMwIgRI6wei4uLw1dffWWy729/+xv69euHEydOoGvXrtJ+uVzuUlc9UaAy1NRJT0tDY2MTampqTOrqpKenO/THL5Q4muMUiFPvncnfUqvVmDFjhrTv7LmzCJcnXa7XdIW1ddECOdil0BZQOTu1tbUQBAHx8fEm+xcvXozExET07dsXS5YsQUtLi93naWpqQl1dncmDKJAYaupsKhiI/754J757+Q/StOGSkhJ8//33Pm6h/3E0xykQp9472uZjx45ZzesBgHPnz0k5S44mcdsaViPyNwET7DQ2NuKpp57CuHHjEBsbK+2fMWMG1q5di82bN+Phhx/GwoUL8eSTT9p9rkWLFiEuLk56ZGRkeLr5RB4VTMNNnuLIlPqMjAxkZ2d7uWXt19a9GaxcudJm7R5ZdGfM2vIr+jz3CWZsrIdu2BMmx4MhiZtCV0AEO5cuXcIf//hHiKKIFStWmBybPXs2cnJy0Lt3bzzyyCP4y1/+gmXLlqGpqcnm882dOxe1tbXSo6KiwtO3QEQ+5siU+sLCwoAMFB0tF2A+wcPkvDAZhE6dUXtJQLg8yaLuTqAkcRNZ4/fBjiHQ+eWXX/DVV1+Z9OpYk5WVhZaWFhw/ftzmOZGRkYiNjTV5EFHwszelvqioCCqVykctaz9b95aenm73Ol3DBanmTkv9WYh624FMICdxU2jz6+UiDIHOzz//jM2bNyMxMbHNaw4cOICwsDAkJyd7oYVEFGhUKhVyc3MRFxcHoDXHydvJt+ZLQWi1Wrc8r7V7GzRokLRtjfkSEemPrUK4PMnu6wRiEjeFNp8GOw0NDTh69Ki0XV5ejgMHDiAhIQGpqanIz8/H/v378cknn0Cn00mVYBMSEhAREYGysjLs3r0bQ4cOhVwuR1lZGWbNmoUJEyagc2eWPqfQxZk09gVzjpP5vRlLT0/HyZMnrebtCIKApCT7QY5BICZxU2jzabCzd+9eDB06VNqePXs2AOC+++7DCy+8gI8//hgAcOONN5pct3nzZuTk5CAyMhJr167FCy+8gKamJnTv3h2zZs2SnocokI1ath1n6k1zz07XN7Z5XXFxsUmS/siRI6FUKrF06dKAHqYJZp5c9NPYq6++igkTJlxeJ+vK6xnyepYvX44nSm3nOwqCAKVSGZBJ3BTafBrs5OTk2P0Fb+uX/3e/+x127drl7mYR+QVDAUFnTZgwweJ3xzCTJtDzUqh9Ro8ejaKiIsyYMcNk+rlSqURhYSFUKhVeOvgJai9ZXhvoSdwU2vw+QZko1IUJgCI2SnqkyCNMCgiaC+TlEMjzbC0DYgiCO3bsCACQmVXmDoYkbgpdfp2gTERAl5gI7P5/uQBa89wAICYmz+nnMZ5Jk5OT484mkh9rK3/LVs5SiiIFJy7/7IskbiJ3Ys8OUYjhTJrQUVxcbLUScnFxcZvXnm1oRvpjq5D+2Cq8sD8cg17ZglHLtnuyuUQew54dIjfQarWIiYkB0Nr74s+l8zmTJnTYyt+aMGFCm9fqRUhT0KvruVgoBTYGO0RBxnymjfF+zqQJLbbyt+wtK9FFHnn5PD00lysuR8QmQe/5yWJEHsNgh8iPGPcQ9XvpS5efx9bUYmdn0nhrSjR5l733dMP0wQAs/y+yd4cCGXN2iAKMecKp+eyq1atXB+VyCERErmKwQxRAHEk4HT16tN2pxUREoYbBDlEAmTBhgkkxOOBKwumHH34IURQRHR0d1MshkONs5ebYy9khCkbM2SHyIeO8iJGFW6TpvgBwpsEyR8JewmlBQQFGjx7NwIZM2MrfIgol7Nkh8hNnG5pRXd+McHkSwuXOzX4xLhhIZGArf2v16tU+ahGRbzDYIfIzol6HlvqzSJFHQBEbhaSYCIevZcFAMmYrf2v06NE+bBWR93EYi8jP6LQXoHlzEo5cLk6o1WoRU+DYtSwY6JhgnlJvfm9arVb6ub35W6frG9F/4UZpu4s8UpqqTuTPGOwQBRAWDCRf0otAVV2jr5tB5DQGO0QBxl0FA4kclRQTAUG4kvVwur6RFZUpoDDYIfJzxkUDn3nmGaxcuRInL5fxB1oTTgsLC1lHhxzm7DDeBw/ebLLeW/+FG9nDQwGFCcpEXqLVaiEIAgRBMMmjsEetVpsUEXz55ZdNjrNgIBFR2xjsELlBW0s4uKK4uBj5+fkWRQSNZ1yxYCB5iqH3x1CokiiQuRTsvPPOO/j000+l7SeffBLx8fEYOHAgfvnlF7c1jigQmPe+GJZwUKvV7XreJ5980mYRQQN3BFVERMHOpWBn4cKF6NixIwCgrKwMy5cvx6uvvoqkpCTMmjXLrQ0k8mdqtdpq74tGo0F+fn67Ah7z57Rmx44dLj8/EVGocCnYqaiowDXXXAMAWL9+PcaOHYspU6Zg0aJFrOBKIUOn02HmzJl2e18KCgoc7n252Oh8wmdVVZXT1xARhRqXgp2YmBicO3cOAPDll1/i97//PQAgKioKFy9edF/riPxYaWkpKisrbR53dgmH8+fPO90GhULh9DVERKHGpannv//97/Hggw+ib9+++OmnnzBy5EgAwPfff4/MzEx3to/Ibzm6NIO18/74f3txTtsCUdRLC3/Kojs73YZBgwZZ3R/MFYKJiJzlUs/O8uXLMWDAAJw5cwYffvghEhMTAQD79u3DuHHj3NpAIn/l6NIM1s4729CMqrpGk4U/hTDHZlUZr1rNmVhERG1zqWenrq4Ob7zxBsLCTGOlF154ARUVFW5pGJG/y87OhlKphEajcXkJB1Gvh05rOnyla7hg93XT09PtDp8ReYv5WlkA18si/+RSsNO9e3ecOnUKycnJJvvPnz+P7t27czoshQSZTIalS5ciPz/f6SUcWpORw6DTnofmzUkOv2ZJSQkGDRqEuLi49jafqN24VhYFCpeGsWzlAjQ0NCAqKqpdDSIKJCqVCkVFRUhLSzPZr1QqUVRUZLOycW1NrcOvYTxsxSKC5Cx3FQc0rgCeGB0ORWyUySNMaPs6RyuHE7mbUz07s2fPBtD64fv888+jU6dO0jGdTofdu3fjxhtvdGsDifydSqVCbm6u1NtSUlKCvLw8u0GJTq9z6JfPONAh8hfma2UBXC+L/JtTwc63334LoLVn57vvvkNERIR0LCIiAn369METTzzh3hYSBQDjwMadvS9KpRKLFy/G+PHj3fJ8REShyKlgZ/PmzQCA+++/H0uXLkVsbKxHGkVEwOLFi/HEE0+g0YVig0REdIVLCcorV650dzuIgp5x4r75TEZrHn30UebnEBG5gUvBjlarxeLFi7Fx40acPn0aer3e5Pj//vc/tzSOKFio1WrMmDFD2tbr9VZnBxjP6rIV6LBgIBGRc1wKdh588EFs3boVf/7zn5GamsokSiI7DIuFOhKgsIYOBbpKTSUEQUBDQ0O7Zn8RuZNLwc5nn32GTz/91GapeiJqZW+xUHOsoUNE5BkuBTudO3dGQkKCu9tCFHTaWizU2JAhQzzcGiKi0ORSUcEXX3wRzz//PH799dd2vfi2bdswatQopKWlQRAErF+/3uS4KIp4/vnnkZqaio4dOyI3Nxc///yzyTnnz5/H+PHjERsbi/j4eEyePBkNDQ3taheRuzi6WCgREXmOSz07f/nLX3Ds2DGkpKQgMzMTHTp0MDm+f/9+h55Hq9WiT58+eOCBB6xWmn311Vfxxhtv4J133kH37t3x3HPPYfjw4Th8+LBUqXn8+PE4deoUvvrqK1y6dAn3338/pkyZgjVr1rhya0RuZVgEVDHxdchirqxq7soK50RE5BqXgp0xY8a45cVHjBiBESNGWD0miiIKCwvx7LPPYvTo0QCAd999FykpKVi/fj3uuece/PDDD/j888+xZ88e3HzzzQCAZcuWYeTIkXjttdcsSvgTeZthsVAxpjPC5Um+bg4RUUhyKdiZN2+eu9thoby8HFVVVcjNzZX2xcXFISsrC2VlZbjnnntQVlaG+Ph4KdABgNzcXISFhWH37t246667rD53U1MTmpqapO26ujrP3QiFLK1Wi5iYGABA+uV9ol4HnfbKqubGPxMFA1l0Z6Q/tgq3Fe6EIIQhMdqlPzNEbuVSzg4A1NTU4P/+7/8wd+5cnD9/HkDr8JVGo3FLw6qqqgAAKSkpJvtTUlKkY1VVVRYrr4eHhyMhIUE6x5pFixYhLi5OemRkZLilzUS2GBL6ddoL0Lw5CZo3JyFsw3P4S15yG1cS+Qfjopjbtm0z2TYmhMkQLk9CdX0zquoacaah2aHriDzJpWDn4MGDuPbaa/HKK6/gtddeQ01NDYDWeiJz5851Z/s8Yu7cuaitrZUeFRUVvm4SBbmOl3PMDEpKSlBeXi4N0drjrlWriVylVqvRs2dPaXvkyJHIzMyEWq2W9nWRRyJFHoGW+rNoqT8rrYJeXVVt9zoib3Ap2Jk9ezYmTZqEn3/+WUoUBlr/I2/bts0tDVMoFACA6upqk/3V1dXSMYVCgdOnT5scb2lpwfnz56VzrImMjERsbKzJg6g9nA1I3LlYKJEnGYpimvfaazQa5OfnS4HLhumDsalgoNRzGRPeWllfp9fZvY7IG1wKdvbs2YOHH37YYn96errd4SNndO/eHQqFAhs3bpT21dXVYffu3RgwYAAAYMCAAaipqcG+ffukczZt2gS9Xo+srCy3tIOIKFTZK4pp2FdQUGB1aKq2ptbqc7Z1HZEnuBTsREZGWk3q/emnn9ClSxeHn6ehoQEHDhzAgQMHALQmJR84cAAnTpyAIAgoKCjASy+9hI8//hjfffcdJk6ciLS0NGk22PXXX4/bb78dDz30EL755hvs2LED06ZNwz333MOZWBSQOGRF/qStopiiKKKiogKlpaUWx8x7dBy9jsgTXAp2/vCHP2DBggW4dOkSgNbFC0+cOIGnnnoKY8eOdfh59u7di759+6Jv374AWofH+vbti+effx4A8OSTT2L69OmYMmUKbrnlFjQ0NODzzz83GTp7//33cd1112HYsGEYOXIkBg8ejH/84x+u3BYRERlxtCimq8UzWXSTvMXlooL5+flITk7GxYsXceutt6KqqgoDBgzAyy+/7PDz5OTk2F0zSBAELFiwAAsWLLB5TkJCAgsIEhF5gKEopqPnOTss5ejzE7WXS8FOXFwcvvrqK2zfvh0HDx5EQ0MDfve735nUxCEiosBmKIqp0WisfjEVBAFKpRLZ2dlQq9WYMWOGQ89rfB2RN7gU7FRUVCAjIwODBw/G4MGD3d0mopBhyNEh8kcymQxLly5Ffn4+BEEw+b8qCK1zywsLC1FcXIz8/HyH/i8bX8cZieQtLuXsZGZm4tZbb8U///lPXLjACrBERMFKpVKhqKjIYtKHUqlEUVERRo8ebXPGljWG66yth0jkKS4FO3v37kW/fv2wYMECpKamYsyYMSgqKjJZgoGIiIKDSqXC4cOHpW1DUUyVStXmjC1jxtcReZNLwU7fvn2xZMkSnDhxAp999hm6dOmCKVOmICUlBQ888IC720gUkIyTNZuamu2cSeT/jIecjItiOjOjisU0yVdcXhsLaB17HTp0KP75z3/i66+/Rvfu3fHOO++4q21EAcu8vP7Zc2d92Boiz+GMKgoE7VqOtrKyEmvWrMGaNWtw6NAhDBgwAMuXL3dX24gC0uAFG3C8uh4Y/bK02rksurNP20TkKW3N2CLyBy4FO3//+9+xZs0abN++Hddffz3Gjx+P4uJidOvWzd3tIwooOp0OFWdrES5PavM8omBgb8YWkb9wKdh56aWXMG7cOLzxxhvo06ePu9tEFLBKS0uh0+kQDkDU66DTms5W1DW0bu/fvx85OTnebyCRBxhmbM2YMcNiwdAO8kSkP7YKAHBb4U4IQhi6yCOxYTrLlpD3uBTsnDhxAtu3b8eSJUvwv//9D+vWrUN6ejree+89dO/enbV3KGQZJ2vqtBegeXNSm+cRBQOVSoXc3FzExcUBAJISk1DTDIgQpJ7O6nom6pNvuJSgrFarMXz4cHTs2BH79++XppzX1tZi4cKFbm0gUSBxtrw+UTAxnmmVnhgDRWwUUuQRaKk/i5b6swgTfNg4CmkuD2O99dZbmDhxItauXSvtHzRoEF566SW3NY4o0GRnZ0Omtl1zhGXyKVR88ODNiI6OhlarRUxMDACg30tfsneHfMKlnp0jR45gyJAhFvvj4uJQU1PT3jYRBSyZTIb4uHirx1gmn4jIN1wKdhQKBY4ePWqxf/v27bjqqqva3SiiQNaxY0cAgCzMNKBhmXwiIt9waRjroYcewsyZM/Gvf/0LgiDg5MmTKCsrwxNPPIHnnnvO3W0kCkgpihScuPxzSUkJ8vLy2KNDROQDLgU7Tz/9NPR6PYYNG4Zff/0VQ4YMQWRkJJ544glMnz7d3W0kCkjGuZgsk09E5DsuDWMJgoD/9//+H86fP49Dhw5h165dOHPmDF588UV3t4+IiPxAdHQ0RFGEKIqIjo526TpBaNcKRUQua9dyERERESbr/xARERH5G4bZREREFNQY7BAREVFQY7BDREREQY3BDhEREQW1diUoExERGRhmXhH5GwY7RO00atl2nKlvkrZP1zf6sDVERGSOwQ5RO52pb0JVHQMcIkedrm9E/4Ubpe0u8khsmD7Yhy2iYMdgh8hNwgQgWR4lbSdG89eLyBq9CH5BIK/ipzGRmyTLo7DrmWHStlarRUyB79pD5G+6yCNNtk/XN0LPFB/yAgY7RB7CZE0iU+ZDVf0XbmQPD3kFp54TERFRUGOwQ+QCrVYLQRAgCAJEUe/r5hARkR0MdoiIiCioMdghIiKioMZgh4iIiIIagx0iIiIKagx2iIiIKKj5fbCTmZkpzXoxfkydOhUAkJOTY3HskUce8XGriYjIGs5kJF/w+6KCe/bsgU6nk7YPHTqE3//+97j77rulfQ899BAWLFggbXfq1MmrbSQiIiL/5ffBTpcuXUy2Fy9ejKuvvhq33nqrtK9Tp05QKBTebhoREREFAL8fxjLW3NyM1atX44EHHoAgCNL+999/H0lJSbjhhhswd+5c/Prrr3afp6mpCXV1dSYPIluMu921Wq2vm0MUdERRz98x8ii/79kxtn79etTU1GDSpEnSvnvvvRfdunVDWloaDh48iKeeegpHjhyBWq22+TyLFi3C/PnzvdBiClbGQ6tNTc0+bAkREbUloIKdt99+GyNGjEBaWpq0b8qUKdLPvXr1QmpqKoYNG4Zjx47h6quvtvo8c+fOxezZs6Xturo6ZGRkeK7hFFTUajVmzJghbZ89dxbh8iRcvHjRh60iIiJbAmYY65dffsHXX3+NBx980O55WVlZAICjR4/aPCcyMhKxsbEmDyJHFBcXIz8/HxqNxuLYufPn7PYoEhGRbwRMz87KlSuRnJyMO+64w+55Bw4cAACkpqZ6oVUUauZurkHaoytN9smiO0s/FxQUYPTo0ZDJZN5uGlHAOtPQjPTHVgEAbivcCUEIQxd5JDZMH+zbhlHQCIhgR6/XY+XKlbjvvvsQHn6lyceOHcOaNWswcuRIJCYm4uDBg5g1axaGDBmC3r17+7DFFKx0EdEIlyfZPF5RUYHS0lLk5OR4r1FEAU4vQvq9qq5nDhy5X0AEO19//TVOnDiBBx54wGR/REQEvv76axQWFkKr1SIjIwNjx47Fs88+66OWUqgQ9TrotBdM9ukaWrdPnTrliyYRBZykmAgIQhhEUQ/NyZMAgIjYJOhFHzeMgk5ABDt5eXkQRcv//RkZGdi6dasPWkShTqe9AM2bk6we4xAqkW3GMxmnXfsr8vLy0NjYiJiYGABAv5e+ZO8OuV3AJCgT+QNZmO1cHEEQkJGRgezsbC+2iChwqNVq9OzZU9oeOXIkMjMzUVxc7MNWUSgIiJ4dIn8RFx+HukuW+w1FLgsLC5mcTGSFWq1Gfn6+RS+9RqPBhAkTfNQqChXs2SFqg3G3e5jQ+itj3sOjVCpRVFQElUrl1bYRBQKdToeZM2daTUcQRdFkP4t0kicw2CGyw7zb/ey5swCAuLg4aV9JSQnKy8sZ6BDZUFpaisrKSofONfyOsUgnuRODHSIbDN3u1goInr9wXvp5yJAhHLoissOVGYos0knuxGCHyAp73e5E5BxXZygWFBSYDCMTuYrBDpEVznS7E5F92dnZUCqVUiK/oyoqKvDll196qFUUShjsEFnBwoBE7iOTybB06VIAcDrgqaqq8kSTKMQw2CGygoUBidxLpVKhqKgIaWlpTl2nUCg81CIKJQx2iKxwtdudiGxTqVQ4fPiwtJ2UlNTm79igQYM83SwKAQx2iKxoT7c7EdlmPHPRkd8xznQkd2CwQ2SDvW73hIQEH7SIKLiMHj3apaEtImcx2CGyw6LbPTEJANAxKspXTSIKKrZ+x4jcicEOURuMu9EjIyN82BKi4MTfMfI0LgRKZMeoZdtxuu4i0h9bBQA409C6bo8ghLHgIJEHyaI7I/2xVbitcCcEIQxd5JHYMH2wr5tFAYrBDpEdZ+qbUF3fjHB5a9e6nvENkVcIYTKEy5NQXc+FQan9OIxFZEar1UIQBAiCAFHUAwBEvQ4t9WeRIo+AIjYKXeSRPm4lUXAKb/kVovYCWurPoqX+LER963IRXBiU2oM9O0QO0GkvQPPmJBxpaEB0dLSvm0MUtMrm32UyRJz+2CqEy5OkhUFVKpUPW0eBij07RETkN+zlwnFhUHIVgx0iIvKa6OhoiKIIURSd7iWtqKhAaWmph1pGwYzBDhERBQwu0kuuYLBDREQBg4v0kisY7BARkd+wt05WRkYGsrOzvdgaChYMdogcoExXupRjQERtM+TxfPjhhwBsBzyFhYVcGJRcwmCHiIj8gr3FdxMTEjntnFzGYIfIjPHU1qYmVm8l8iabi+927OirJlEQYLBDZEStVqNnz57S9tlzZwGweiuRN3FhUHI3BjtEl6nVauTn50Oj0VgcM1RvJSKiwMNghwitQ1czZ85k9VYioiDEYIcIQGlpKSorK+2ew+qtRESBicEOERyvysrqrUREgYfBDhEcr8rK6q1ERIEn3NcNIPIH2dnZUCqV0Gg0NvN2WL2VyHdO1zdCOfUdAEB6WhoEIQxd5JHYMH2wj1tGgYDBDhFap7p2e+hNiNXnTfdHd5Z+ZvVWIt/Ri0C4vLXmTnU961+RcxjsEF3WEt4J4XLrI7ut1Vvv9HKLiCgpJgKCEAZR1ENz8iQAICI2CXrbEyeJLPh1zs4LL7wAQRBMHtddd510vLGxEVOnTkViYiJiYmIwduxYVFdX+7DFFAzCBKCl/ixa6s8iPgJQxEaia3K8r5tFFDIMa2WJoohPZ96KXc8Mw6aCgdC8OQmaNyehSwwLDZJz/L5n57e//S2+/vpraTs8/EqTZ82ahU8//RTr1q1DXFwcpk2bBpVKhR07dviiqRQkusRE4JvFkwAARxoauPgnkZ+q1FRCEAQ08PeU2uD3wU54eDgUCoXF/traWrz99ttYs2YNbrvtNgDAypUrcf3112PXrl3o37+/t5tKREREfsivh7EA4Oeff0ZaWhquuuoqjB8/HidOnAAA7Nu3D5cuXUJubq507nXXXYeuXbuirKzM7nM2NTWhrq7O5EFERETBya+DnaysLKxatQqff/45VqxYgfLycmRnZ6O+vh5VVVWIiIhAfHy8yTUpKSmoqqqy+7yLFi1CXFyc9MjIyPDgXRARUXsZL9XS1MTZWOQcvw52RowYgbvvvhu9e/fG8OHDUVJSgpqaGnzwwQftet65c+eitrZWelRUVLipxURE5G5qtRo9e/aUts+eO+vD1lAg8vucHWPx8fG49tprcfToUfz+979Hc3MzampqTHp3qqurreb4GIuMjERkZKSHW0tERO2lVquRn59vd5Feorb4dc+OuYaGBhw7dgypqam46aab0KFDB2zcuFE6fuTIEZw4cQIDBgzwYSspkGi1WqmsgSjqfd0cIjKi0+kwc+bMNgMd4yEuImv8Oth54oknsHXrVhw/fhw7d+7EXXfdBZlMhnHjxiEuLg6TJ0/G7NmzsXnzZuzbtw/3338/BgwYwJlY1C6tBcxaa3xwOiuR75SWlqKysrLN81asWMGAh+zy62CnsrIS48aNQ48ePfDHP/4RiYmJ2LVrF7p06QIAeP3113HnnXdi7NixGDJkCBQKBdRqtY9bTURE7nDq1CmHznv66aeRmZnJz3+yya9zdtauXWv3eFRUFJYvX47ly5d7qUVEROQtqampDp+r0WiQn5+PoqIiqFQqD7aKApFf9+wQEVHoys7OhlKphCAIbZ5ryOspKCjgkBZZYLBDRER+SSaTYenSpQDgcMBTUVGB0tJSTzeNAgyDHQppLFRG5N9UKhWKioqQlpbm8DWO5vpQ6GCwQyHLVqGyixcv+qpJRGSFSqXC4cOHHT7fmVwfCg1+naBM5ClqtRpTPz4B2eiXkX55nyy6MwDg3PlzUKvVTHIk8iMymcxyX3RnpD+2StrWaS+gw6a/Ijs724sto0DAnh0KOYZCZbLozgiXJ0kPIezKhymTHIn8nxAmM/kdlkV3RkVFBcLDw6HVan3dPPIjDHYo5JgXKhP1OrTUn5UeuoYLTHIk8mPdkuMhai9Iv7OivvWLSUJCgo9bRv6Kw1gUcsyTF3XaC9C8OanN84jIP3z5RC50Oh3i4uIAAH2e+wQ1zUDHqCgft4z8FXt2KOQ4mrzIJEci/2WcwxMZGeHDllAgYM8OhRxDoTJbSwsKggClUskkRyKiIMGeHQo5xoXKzBkKlxUWFlqd/UFERIGHwQ6FJJVKhcSERIv9SqWSa+sQ+aHo6GiIoghRFBEdHe3r5lCAYbBDIatjx44m2yUlJSgvL2egQxSgjIemt23bxvIRJGGwQ3TZkCFDOHRFFMCqq6qln0eOHInMzEyo1Woftoj8BYMdIiIKOMbDWo2NTQAAnd60J0ej0SA/P58BDzHYISKiwKXT6VBTW2P1mCEYGjt2LOrq6rzbMPIrDHaIiChglZaWOpSbs2PHDi+0hvwVgx0iIgpYjlY6r6qq8nBLyJ8x2CEiooDlaKVzhULh4ZaQP2OwQyFPma5k7Q6iAJWdne3QLMpBgwZ5oTXkr7hcBIWEUcu240x9k8m+0/WNPmoNEbmLTCZDfFw8ai8BsujOSH9slclxXcMFVL07i2UlQhyDHQoJZ+qbUFXH4IYoGHXs2BG1lxohhMkQLk+yes62bduQl5fHoCdEcRiLQkqYAChio0weXeSRvm4WEbVDF3kkFLFRSJZHoKX+LFrqz0IwW+qXRQZDG3t2KKQky6Ow65lhvm4GEbnRhumDAQBarRYxMTEAgPTHVln08hiKDHL9u9DDnh0iIgoJotja21NQUMB1s0IMgx0KKaKohyAIEAQBWq3W180hIi8TRREVFRUoLS31dVPIixjsEBFRyHG0GCEFBwY7REQUchwtRkjBgcEOEREFhejoaLS0tECpVNo8RxAEZGRkIDs724stI19jsENEREFDJpNh6dKlVo8JggAAKCwsZL2dEMNgh4KWVquVkpFFUQ8AJpU3tm3bxhkZREFIpVIhMSHRYr9SqeS08xDFYIdCSnVVtfQzi4wRBa+OHTuabJeUlKC8vJyBTohisEMh4WJj61IROr1pT46hyBgDHqLgNmTIEA5dhTAGOxQSamtqre5nkTEiouDH5SIoaBkHLzq9zuZ/duMiYzk5OV5pGxF5h2El9NsKd0IQwtBFHiktL0Ghw697dhYtWoRbbrkFcrkcycnJGDNmDI4cOWJyTk5OjpSEang88sgjPmox+YvBCzag1//7GOmPrUL6Y6sgi+7c5jUsMkYUfAwroVfXN6OqrhFn6pt83STyAb/u2dm6dSumTp2KW265BS0tLXjmmWeQl5eHw4cPIzo6WjrvoYcewoIFC6TtTp06+aK55CfUajWOV9cjXJ7k1H9wFhkjCh5d5JEm26frG6EXbZxMQc+vg53PP//cZHvVqlVITk7Gvn37MGTIEGl/p06doFAovN088kM6nQ4zZ84E/vASAEDU66DTXrhyvOGCxTWCIECpVLLIGFEQMR+q6r9wI6rqGn3UGvI1vw52zNXWtiaZJiQkmOx///33sXr1aigUCowaNQrPPfec3d6dpqYmNDVd6cqsq6vzTIPJq7RaLWJiYgAA6Zf36bQXoHlzks1rWGSMKLQYFgMGgIaGBpNRAgpeARPs6PV6FBQUYNCgQbjhhhuk/ffeey+6deuGtLQ0HDx4EE899RSOHDlidyrxokWLMH/+fG80m/ycUqlEYWEha28QEQWxgAl2pk6dikOHDmH79u0m+6dMmSL93KtXL6SmpmLYsGE4duwYrr76aqvPNXfuXMyePVvarqurQ0ZGhmcaTn6rpKQEeXl57NEhIgpyfj0by2DatGn45JNPsHnzZrsLvAFAVlYWAODo0aM2z4mMjERsbKzJgwKfI3VyDN3XAIuMEYUiLhkTmvw62BFFEdOmTcNHH32ETZs2oXv37m1ec+DAAQCcWRNq1Go1evbsafcc40CHiEITl4wJTX4d7EydOhWrV6/GmjVrIJfLUVVVhaqqKly8eBEAcOzYMbz44ovYt28fjh8/jo8//hgTJ07EkCFD0Lt3bx+3nrxFrVYjPz8fGo3G7nlKpRKrV6/2UquIyJ8Y/m5wyZjQ5NfBzooVK1BbW4ucnBykpqZKj//85z8AgIiICHz99dfIy8vDddddh8cffxxjx47Fhg0bfNxy8hbDVHPDsg+2fPLJJygvL8fo0aO91DIi8hc6nQ41tTVWj3HJmNDg1wnKbf0By8jIwNatW73UGvJHpaWlqKysbPO8sLAwyGQyREdHt/n/ioiCS2lpKXQ6LhkTyvw62CFqi6NLPFRVVXm4JUTkr4w/JwxrZRnTNVxA1buzuGRMEGOwQwEtNTUViomvQxZjuvaV+VpYrLBNFLpaJ6z8AuDKWlm2z6NgxGCHApJxteSu096DYGehz4yMDOTl5XmraUTkZ7KzsyF7/zBa6k33y6I7QwhrLT+RkZHBJWOCGIMdCnhx8XGou2S5DhbQulzEG1wKgiikyWQyvD5Cgfz8fABX8kHTH1sl9fJwyZjgxmCHApLxrIkw4fKkwot1JutgZWRk4A0uBUFEAFQqFYqKijBjxgyLMhWJCYlQqe70UcvIG/x66jmRNeYFBM+eOwsAiIuLk/aVlJSgvLycgQ4RSVQqFQ4fPixtJyW29up07NjRV00iL2HPDgUUQwFBa9PHz184L/3MpSCIyBrjz4XIyAigudmHrSFvYc8OBQxHCwgSEREZY7BDAcPRAoJERM6o1FRCEARotVpfN4U8hMEOBQxHC36tWbMG0dHRHm4NEQUiQxV1URQhCPwTGCqYs0MBw9GCXywMRkTOMFRVHvTKFkRFRaKLPAobpg/2dbPIjRjsUMDIzs6GUqlEy7DHTSokG//MwmBE5KjWldAFqapyTTOA5iZcvNjo66aRmzHYoYBgXDE5PbqzzXLvLAxGRI5Qq9U4XXHC4ouTECbDufPnIAgCAKChoQEApM+fhoYGDpMHIAY7FBCMiwjGyuX4FaYVk2UyGTJTEqBSjfJRC4koUBhmdlaZTXgwrqhssG3bNvTv399kOy8vj1+qAgyzs8jvmRcRrKtvXeBGp70AzZuT8M87k/G/18dh+/MMdIiobc7M7Bw5ciSSkpJMtjMzM6FWqz3VPPIABjvkd+rq6iAIAgRBwLPPPov8/HyL8u7GWECQiJzh6MxOA71eb7Kt0WiQn5/PgCeAMNghv2Lei/Pyyy+3WUTQeIiLiKgt7Z2xafhMKigo4OdPgGCwQ37DsBSEvV4ca/bv3++hFhFRMDLM7DQkIbtCFEVUVFSgtLTUjS0jT2GwQ36hPUtBONslTUShTSaTYenSpQDQroAH4OdPoOBsLPILthIGFRNfhyyms8k+46miAIsIEpHzVCoVioqKMGPGDIveZEORQWO6hguoeneWxfPw8ycwMNghv2Dr25EsxnZNHYBFBInIdSqVCrm5uYiLiwMAhIW1DnYYigzaIwgClEolP38CBIexyC+09e1I1OvQUn/W5KHTXmARQSJql9jYWGmtrK5d4i0+Z0S9ZQKyYeiLnz+Bgz075BcMCYMajcZq3o6hpo5BRkYG3igshEql8mIriSiYbX9+FNRqtcnQlrVCg0qlEoX8/Ako7Nkhv+BMwmBJSQnKy8v5QUNEbqdSqXD48GFpOynRNNDh509gYrBDfsOQMJiWlmZxLCEhQfqZRQSJyJOMP18iIyNMjvHzJzBxGIv8ykpNMtIeXQmcPAkA6CBPggigU8dOLk1LJyJyVnR0tPR503/hRgBARGwSsl7+GsOW7gIAdJFHYsP0wT5rIzmHwQ75lTP1TThd3yyNkTO8ISJ/oBeBqrpGm8d1Oh1KS0tx6tQppKamSrO0zPexV8g3GOyQXwoTgGR5lLTdRR7pw9YQUagy/+w5Xd8Ivdm3MLVajZkzZ5rUCktMTAQAnDt3TtqnVCqxdOlS5vv4AIMd8ipr336sfdNJlkdh1zPDfNBCIqIrzIeq+i/ceLmHR8SWLVtQXFyMwsJCi+uMgxwDwwKiRUVFDHi8TBCZCIG6ujrExcWhtrYWsbGxvm5O0DL/9qOY+Doi4pIQHxePjh07ArjyrUkRy2CHiPyPIdgR9XrotOdNjtmqsmzMUIywvLycQ1pu4Ojfb/bskFcYFvk0jq1lMZ0hdOqM2ktA7SXbY+FERP7i4sWLAAQIYWFtVlm2xngB0ZycHLe3j6xjsEMe19Yin6JeBzTWIVWhANBaY4c5OkTkb3Q6HWqqTkAXEW2yXxbdGUKYc700XEDUuxjskMsczb8xLPJpvqinYUFPQ3XkdzZv5jcdIvJbpaWlOPF/Uy32G6osmy8gam9Yq7q6GjqdzuIz09HPVXIOiwqSS9RqNTIzMzF06FDce++9GDp0KDIzM6FWqy3ONXyDMSzqaXiYfxPiNx0i8mdtfUYZFhA1PIy/3JmbNWuWxWemM5+r5Bz27HiYI7UXBg4ciJ07d7b7HE8+t/E5P//8M1544QWLYanKykqMHTsWBQUF+LZLHhrRAQDQ0NAR6Y+tknpyRL0OOu2FK/9GDa0/Hz58GFu2bOE3GSLyS7YWLDZ8hhkYhrXMe3oM5xp6ezQaDcaOHYv58+fjwoULVmd1GZ/zm9/8xmOf6974++TLz/WgmY21fPlyLFmyBFVVVejTpw+WLVuGfv36OXStp2ZjOVp7QSaTQafTtfscTz63+TltsbZ4nkFL/VmTRT3NsRYFEfkjnU6HzMxMmwsWG7Tn888Rnvpc9+Q5nvpcd/Tvd1AEO//5z38wceJEvPXWW8jKykJhYSHWrVuHI0eOIDk5uc3rPRHsWJt9FMys5eMIYTKLXhyg7emZhoVAWYuCiPyN4bMdgNXPd8ue7QbU1Nba/Ex0ZLp6MPDU53pIBTtZWVm45ZZb8Le//Q0AoNfrkZGRgenTp+Ppp59u83p3BzuG6N+4RydQmActjnL3txjWoiAif2Wt1z4jIwOFhYUWf8j//e9/4957722zt8dZgRgkeeJzPWTq7DQ3N2Pfvn2YO3eutC8sLAy5ubkoKyuzek1TUxOampqk7draWgCt/2juYJh9BADyW8Yg9pYxbnleb3ClboQ5419c87FsRxlqUXz++efS+C8RkT/Izc3FwYMHsXPnTlRVVUGhUGDgwIGQyWQWf0cMf4DNPwuNP2td+dwNlydZ5AP5s7o961G/Z73bP9cN/95t9tuIAU6j0YgAxJ07d5rsnzNnjtivXz+r18ybN09E6xqTfPDBBx988MFHgD8qKirsxgoB37Pjirlz52L27NnStl6vx/nz55GYmCiNK7pDXV0dMjIyUFFREbTLUAT7PfL+Al+w3yPvL/AF+z168v5EUUR9fT3S0tLsnhfwwU5SUhJkMhmqq6tN9ldXV0OhUFi9JjIyEpGRphV64+PjPdVExMbGBuV/YGPBfo+8v8AX7PfI+wt8wX6Pnrq/uLi4Ns8J+KKCERERuOmmm7Bx40Zpn16vx8aNGzFgwAAftoyIiIj8QcD37ADA7Nmzcd999+Hmm29Gv379UFhYCK1Wi/vvv9/XTSMiIiIfC4pg509/+hPOnDmD559/HlVVVbjxxhvx+eefIyUlxaftioyMxLx58yyGzIJJsN8j7y/wBfs98v4CX7Dfoz/cX1DU2SEiIiKyJeBzdoiIiIjsYbBDREREQY3BDhEREQU1BjtEREQU1BjstMPLL7+MgQMHolOnTjaLEp44cQJ33HEHOnXqhOTkZMyZMwctLS12n/f8+fMYP348YmNjER8fj8mTJ6OhocEDd+CcLVu2QBAEq489e/bYvC4nJ8fi/EceecSLLXdOZmamRXsXL15s95rGxkZMnToViYmJiImJwdixYy0KXfqD48ePY/LkyejevTs6duyIq6++GvPmzUNzc7Pd6/z9PVy+fDkyMzMRFRWFrKwsfPPNN3bPX7duHa677jpERUWhV69eKCkp8VJLnbNo0SLccsstkMvlSE5OxpgxY3DkyBG716xatcrivYqKivJSi533wgsvWLT3uuuus3tNoLx/gPXPE0EQMHXqVKvn+/v7t23bNowaNQppaWkQBAHr1683OS6KIp5//nmkpqaiY8eOyM3Nxc8//9zm8zr7O+wsBjvt0NzcjLvvvhuPPvqo1eM6nQ533HEHmpubsXPnTrzzzjtYtWoVnn/+ebvPO378eHz//ff46quv8Mknn2Dbtm2YMmWKJ27BKQMHDsSpU6dMHg8++CC6d++Om2++2e61Dz30kMl1r776qpda7ZoFCxaYtHf69Ol2z581axY2bNiAdevWYevWrTh58qTF6sf+4Mcff4Rer8ff//53fP/993j99dfx1ltv4ZlnnmnzWn99D//zn/9g9uzZmDdvHvbv348+ffpg+PDhOH36tNXzd+7ciXHjxmHy5Mn49ttvMWbMGIwZMwaHDh3ycsvbtnXrVkydOhW7du3CV199hUuXLiEvLw9ardbudbGxsSbv1S+//OKlFrvmt7/9rUl7t2/fbvPcQHr/AGDPnj0m9/bVV18BAO6++26b1/jz+6fVatGnTx8sX77c6vFXX30Vb7zxBt566y3s3r0b0dHRGD58OBobG20+p7O/wy5xy2qcIW7lypViXFycxf6SkhIxLCxMrKqqkvatWLFCjI2NFZuamqw+1+HDh0UA4p49e6R9n332mSgIgqjRaNze9vZobm4Wu3TpIi5YsMDuebfeeqs4c+ZM7zTKDbp16ya+/vrrDp9fU1MjdujQQVy3bp2074cffhABiGVlZR5ooXu9+uqrYvfu3e2e48/vYb9+/cSpU6dK2zqdTkxLSxMXLVpk9fw//vGP4h133GGyLysrS3z44Yc92k53OH36tAhA3Lp1q81zbH0e+at58+aJffr0cfj8QH7/RFEUZ86cKV599dWiXq+3ejyQ3j8A4kcffSRt6/V6UaFQiEuWLJH21dTUiJGRkeK///1vm8/j7O+wK9iz40FlZWXo1auXSXHD4cOHo66uDt9//73Na+Lj4016SnJzcxEWFobdu3d7vM3O+Pjjj3Hu3DmHKlW///77SEpKwg033IC5c+fi119/9UILXbd48WIkJiaib9++WLJkid2hx3379uHSpUvIzc2V9l133XXo2rUrysrKvNHcdqmtrUVCQkKb5/nje9jc3Ix9+/aZ/NuHhYUhNzfX5r99WVmZyflA6+9loLxXANp8vxoaGtCtWzdkZGRg9OjRNj9v/MXPP/+MtLQ0XHXVVRg/fjxOnDhh89xAfv+am5uxevVqPPDAA3YXnQ6098+gvLwcVVVVJu9PXFwcsrKybL4/rvwOuyIoKij7q6qqKosqzobtqqoqm9ckJyeb7AsPD0dCQoLNa3zl7bffxvDhw6FUKu2ed++996Jbt25IS0vDwYMH8dRTT+HIkSNQq9VeaqlzZsyYgd/97ndISEjAzp07MXfuXJw6dQp//etfrZ5fVVWFiIgIi7ytlJQUv3vPzB09ehTLli3Da6+9Zvc8f30Pz549C51OZ/X37Mcff7R6ja3fS39/r/R6PQoKCjBo0CDccMMNNs/r0aMH/vWvf6F3796ora3Fa6+9hoEDB+L7779v83fVF7KysrBq1Sr06NEDp06dwvz585GdnY1Dhw5BLpdbnB+o7x8ArF+/HjU1NZg0aZLNcwLt/TNmeA+ceX9c+R12BYMdM08//TReeeUVu+f88MMPbSbQBRJX7rmyshJffPEFPvjggzaf3zjfqFevXkhNTcWwYcNw7NgxXH311a433AnO3OPs2bOlfb1790ZERAQefvhhLFq0yG/LubvyHmo0Gtx+++24++678dBDD9m91h/ew1A3depUHDp0yG4+CwAMGDDAZBHkgQMH4vrrr8ff//53vPjii55uptNGjBgh/dy7d29kZWWhW7du+OCDDzB58mQftsz93n77bYwYMQJpaWk2zwm09y9QMNgx8/jjj9uNugHgqquucui5FAqFRUa5YYaOQqGweY15UlZLSwvOnz9v85r2cuWeV65cicTERPzhD39w+vWysrIAtPYqeOsPZXve16ysLLS0tOD48ePo0aOHxXGFQoHm5mbU1NSY9O5UV1d77D0z5+z9nTx5EkOHDsXAgQPxj3/8w+nX88V7aE1SUhJkMpnFzDd7//YKhcKp8/3BtGnTpMkKzn6779ChA/r27YujR496qHXuFR8fj2uvvdZmewPx/QOAX375BV9//bXTvaGB9P4Z3oPq6mqkpqZK+6urq3HjjTdavcaV32GXuC37J4S1laBcXV0t7fv73/8uxsbGio2NjVafy5CgvHfvXmnfF1984VcJynq9Xuzevbv4+OOPu3T99u3bRQDif//7Xze3zDNWr14thoWFiefPn7d63JCgXFRUJO378ccf/TZBubKyUvzNb34j3nPPPWJLS4tLz+FP72G/fv3EadOmSds6nU5MT0+3m6B85513muwbMGCAXya46vV6cerUqWJaWpr4008/ufQcLS0tYo8ePcRZs2a5uXWeUV9fL3bu3FlcunSp1eOB9P4ZmzdvnqhQKMRLly45dZ0/v3+wkaD82muvSftqa2sdSlB25nfYpba67ZlC0C+//CJ+++234vz588WYmBjx22+/Fb/99luxvr5eFMXW/6Q33HCDmJeXJx44cED8/PPPxS5duohz586VnmP37t1ijx49xMrKSmnf7bffLvbt21fcvXu3uH37dvE3v/mNOG7cOK/fny1ff/21CED84YcfLI5VVlaKPXr0EHfv3i2KoigePXpUXLBggbh3716xvLxcLC4uFq+66ipxyJAh3m62Q3bu3Cm+/vrr4oEDB8Rjx46Jq1evFrt06SJOnDhROsf8HkVRFB955BGxa9eu4qZNm8S9e/eKAwYMEAcMGOCLW7CrsrJSvOaaa8Rhw4aJlZWV4qlTp6SH8TmB9B6uXbtWjIyMFFetWiUePnxYnDJlihgfHy/Ngvzzn/8sPv3009L5O3bsEMPDw8XXXntN/OGHH8R58+aJHTp0EL/77jtf3YJNjz76qBgXFydu2bLF5L369ddfpXPM72/+/PniF198IR47dkzct2+feM8994hRUVHi999/74tbaNPjjz8ubtmyRSwvLxd37Ngh5ubmiklJSeLp06dFUQzs989Ap9OJXbt2FZ966imLY4H2/tXX10t/6wCIf/3rX8Vvv/1W/OWXX0RRFMXFixeL8fHxYnFxsXjw4EFx9OjRYvfu3cWLFy9Kz3HbbbeJy5Ytk7bb+h12BwY77XDfffeJACwemzdvls45fvy4OGLECLFjx45iUlKS+Pjjj5tE9ps3bxYBiOXl5dK+c+fOiePGjRNjYmLE2NhY8f7775cCKH8wbtw4ceDAgVaPlZeXm/wbnDhxQhwyZIiYkJAgRkZGitdcc404Z84csba21ostdty+ffvErKwsMS4uToyKihKvv/56ceHChSY9ceb3KIqiePHiRfGxxx4TO3fuLHbq1Em86667TAIIf7Fy5Uqr/2eNO3kD8T1ctmyZ2LVrVzEiIkLs16+fuGvXLunYrbfeKt53330m53/wwQfitddeK0ZERIi//e1vxU8//dTLLXaMrfdq5cqV0jnm91dQUCD9W6SkpIgjR44U9+/f7/3GO+hPf/qTmJqaKkZERIjp6enin/70J/Ho0aPS8UB+/wy++OILEYB45MgRi2OB9v4Z/maZPwz3oNfrxeeee05MSUkRIyMjxWHDhlncd7du3cR58+aZ7LP3O+wOgiiKovsGxYiIiIj8C+vsEBERUVBjsENERERBjcEOERERBTUGO0RERBTUGOwQERFRUGOwQ0REREGNwQ4REREFNQY7REREFNQY7BAREVFQY7BDREREQY3BDhEREQU1BjtEFHTOnDkDhUKBhQsXSvt27tyJiIgIbNy40YctIyJf4EKgRBSUSkpKMGbMGOzcuRM9evTAjTfeiNGjR+Ovf/2rr5tGRF7GYIeIgtbUqVPx9ddf4+abb8Z3332HPXv2IDIy0tfNIiIvY7BDREHr4sWLuOGGG1BRUYF9+/ahV69evm4SEfkAc3aIKGgdO3YMJ0+ehF6vx/Hjx33dHCLyEfbsEFFQam5uRr9+/XDjjTeiR48eKCwsxHfffYfk5GRfN42IvIzBDhEFpTlz5qCoqAj//e9/ERMTg1tvvRVxcXH45JNPfN00IvIyDmMRUdDZsmULCgsL8d577yE2NhZhYWF47733UFpaihUrVvi6eUTkZezZISIioqDGnh0iIiIKagx2iIiIKKgx2CEiIqKgxmCHiIiIghqDHSIiIgpqDHaIiIgoqDHYISIioqDGYIeIiIiCGoMdIiIiCmoMdoiIiCioMdghIiKioPb/AXXeJYyavdOsAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# construct model representation for comparison with data histogram\n", "a, b, sigma = m.values\n", "\n", "# get expected content per bin from cdf, sum over the individual cdfs\n", "v = np.diff(np.sum(norm.cdf(ax_x.edges[:,np.newaxis],\n", " mu(y, a, b), sigma), axis=1))\n", "\n", "plt.stairs(v, ax_x.edges, label=\"model\", zorder=5, lw=2)\n", "plt.errorbar(ax_x.centers, h1.values(), h1.variances() ** 0.5,\n", " fmt=\"ok\", label=\"data\")\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"events\")\n", "plt.legend(frameon=False);" ] }, { "cell_type": "markdown", "id": "integrated-listening", "metadata": {}, "source": [ "## Fit without conditional variable\n", "\n", "We can also ignore the dependence of $x$ and $y$ and just fit the total $x$ distribution with a model built from the distribution of $y$ values. This also works in this case, but information is lost and therefore the parameter uncertainties become larger than in the previous case.\n", "\n", "On top of that, the calculation is much slower, because building the pdf is more expensive. We parallelise the computation with numba." ] }, { "cell_type": "code", "execution_count": 6, "id": "protecting-monte", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = -8.774e+04 Nfcn = 95
EDM = 1.33e-06 (Goal: 0.0002) time = 5.2 sec
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 a 0.002 0.029
1 b 0.500 0.005
2 sigma 0.98 0.04 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
a b sigma
a 0.000839 4.35e-06 (0.030) -3.16e-05 (-0.027)
b 4.35e-06 (0.030) 2.43e-05 -0.000141 (-0.718)
sigma -3.16e-05 (-0.027) -0.000141 (-0.718) 0.0016
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = -8.774e+04 │ Nfcn = 95 │\n", "│ EDM = 1.33e-06 (Goal: 0.0002) │ time = 5.2 sec │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ a │ 0.002 │ 0.029 │ │ │ │ │ │\n", "│ 1 │ b │ 0.500 │ 0.005 │ │ │ │ │ │\n", "│ 2 │ sigma │ 0.98 │ 0.04 │ │ │ 0 │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────┐\n", "│ │ a b sigma │\n", "├───────┼───────────────────────────────┤\n", "│ a │ 0.000839 4.35e-06 -3.16e-05 │\n", "│ b │ 4.35e-06 2.43e-05 -0.000141 │\n", "│ sigma │ -3.16e-05 -0.000141 0.0016 │\n", "└───────┴───────────────────────────────┘" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nb.config.THREADING_LAYER = 'workqueue'\n", "\n", "\n", "@nb.njit(parallel=True, fastmath=True)\n", "def model(x, a, b, sigma):\n", " mu = a + b * y\n", " total = np.zeros_like(x)\n", " for i in nb.prange(len(mu)):\n", " total += norm_nb.pdf(x, mu[i], sigma)\n", " return total\n", "\n", "\n", "nll = UnbinnedNLL(x, model)\n", "m2 = Minuit(nll, 0.0, 0.0, 2.0)\n", "m2.limits[\"sigma\"] = (0, None)\n", "m2.migrad()" ] }, { "cell_type": "code", "execution_count": 7, "id": "julian-border", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(1, 3, figsize=(8, 2), constrained_layout=True)\n", "for par, axi in zip(m.parameters, ax):\n", " axi.set_title(par)\n", " t = truth[par]\n", " axi.axhline(t, ls=\"--\", color=\"0.5\")\n", " axi.errorbar([\"with\\n conditional\"], m.values[par],\n", " m.errors[par], fmt=\"ok\")\n", " axi.errorbar([\"without\\n conditional\"], m2.values[par],\n", " m2.errors[par], fmt=\"or\")\n", " axi.set_xlim(-0.5, 1.5)\n", " dt = 2 * m2.errors[par]\n", " axi.set_ylim(t - dt, t + dt)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 5 } iminuit-2.24.0/doc/notebooks/cost_function_benchmarks.ipynb0000644000000000000000000034634414332717401021110 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Performance of cost functions\n", "\n", "This is not really a tutorial, but more of a benchmark of the builtin cost functions.\n", "\n", "We test the performance of the cost functions shipped with iminuit. We check that they produce unbiased results with proper variance. To do that, we generate normal distributed data many times and fit a normal distribution to each independent data set. The bias is computed from the averages of these reconstructed parameters. We also compute the mean of the estimated variance for each data set, which should converge to 1.\n", "\n", "Since we do the fit many times, we do not use implementations of the pdf and cdf of a normal distribution from `scipy.stats`, but Numba-accelerated versions from the `numba-stats` package. For the binned fits, we compute histograms of the data with $3 + n/10$ equidistant bins, where $n$ is the sample size.\n", "\n", "Disclaimer: This tutorial is targeted at experts, please read the code to understand what is going on." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Maximum-likelihood fits\n", "\n", "Here we check that the different maximum-likelihood cost functions produce asymptotically unbiased results with the expected variance." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from matplotlib import pyplot as plt\n", "from iminuit import Minuit\n", "from iminuit.cost import (\n", " UnbinnedNLL,\n", " BinnedNLL,\n", " ExtendedUnbinnedNLL,\n", " ExtendedBinnedNLL,\n", " LeastSquares,\n", ")\n", "from argparse import Namespace\n", "import numba as nb\n", "import math\n", "from numba_stats import norm\n", "import joblib" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "n_tries = 100 # increase this to get less scattering\n", "\n", "n_pts = np.array((10, 30, 100, 300, 1000, 3000, 10000))\n", "\n", "truth = Namespace(mu=0, sigma=1)\n", "\n", "\n", "# function that runs random experiments with sample size n\n", "@joblib.delayed\n", "def compute(n):\n", " rng = np.random.default_rng(n)\n", " np.random.seed(n)\n", " u_nll = []\n", " b_nll = []\n", " e_u_nll = []\n", " e_b_nll = []\n", " for i_try in range(n_tries):\n", " while True:\n", " k = 2 * rng.poisson(n)\n", " x = rng.normal(truth.mu, truth.sigma, size=k)\n", " x = x[np.abs(x) < 2]\n", " x = x[:k]\n", " xrange = np.array((-2.0, 2.0))\n", " nh, xe = np.histogram(x, bins=3 + n // 10, range=xrange)\n", " m = [\n", " # model must be a normalized pdf\n", " Minuit(\n", " UnbinnedNLL(\n", " x,\n", " lambda x, mu, sigma: (\n", " norm.pdf(x, mu, sigma) / np.diff(norm.cdf(xrange, mu, sigma))\n", " ),\n", " ),\n", " mu=truth.mu,\n", " sigma=truth.sigma,\n", " ),\n", " # model must be a function that returns the integral over the scaled pdf and the scaled pdf\n", " Minuit(\n", " ExtendedUnbinnedNLL(\n", " x,\n", " lambda x, n, mu, sigma: (\n", " n * np.diff(norm.cdf(xrange, mu, sigma)),\n", " n * norm.pdf(x, mu, sigma),\n", " ),\n", " ),\n", " n=n,\n", " mu=truth.mu,\n", " sigma=truth.sigma,\n", " ),\n", " # model must be a normalized cdf up to an arbitrary additive constant (only differences are used)\n", " Minuit(\n", " BinnedNLL(\n", " nh,\n", " xe,\n", " lambda x, mu, sigma: (\n", " norm.cdf(x, mu, sigma) / np.diff(norm.cdf(xrange, mu, sigma))\n", " ),\n", " ),\n", " mu=truth.mu,\n", " sigma=truth.sigma,\n", " ),\n", " # model must be a scaled cdf up to an arbitrary additive constant (only differences are used)\n", " Minuit(\n", " ExtendedBinnedNLL(\n", " nh, xe, lambda x, n, mu, sigma: n * norm.cdf(x, mu, sigma)\n", " ),\n", " n=n,\n", " mu=truth.mu,\n", " sigma=truth.sigma,\n", " ),\n", " ]\n", " for mi in m:\n", " mi.limits[\"sigma\"] = (1e-3, None)\n", " mi.limits[\"mu\"] = (-2, 2)\n", " if \"n\" in mi.parameters:\n", " mi.limits[\"n\"] = (0, None)\n", "\n", " # only accept a random data set when all fits converged ok\n", " all_good = True\n", " for mi in m:\n", " mi.migrad()\n", " mi.hesse()\n", " if not mi.valid or not mi.accurate:\n", " all_good = False\n", " break\n", " if all_good:\n", " break\n", " print(f\"{n} {i_try} need to re-try {[(mi.valid, mi.accurate) for mi in m]}\")\n", "\n", " # store parameter deviations and estimated variances for each pseudo-experiment\n", " u_nll.append(\n", " (\n", " m[0].values[\"mu\"] - truth.mu,\n", " m[0].errors[\"mu\"] ** 2,\n", " m[0].values[\"sigma\"] - truth.sigma,\n", " m[0].errors[\"sigma\"] ** 2,\n", " )\n", " )\n", " e_u_nll.append(\n", " (\n", " m[1].values[\"n\"] - n,\n", " m[1].errors[\"n\"] ** 2,\n", " m[1].values[\"mu\"] - truth.mu,\n", " m[1].errors[\"mu\"] ** 2,\n", " m[1].values[\"sigma\"] - truth.sigma,\n", " m[1].errors[\"sigma\"] ** 2,\n", " )\n", " )\n", " b_nll.append(\n", " (\n", " m[2].values[\"mu\"] - truth.mu,\n", " m[2].errors[\"mu\"] ** 2,\n", " m[2].values[\"sigma\"] - truth.sigma,\n", " m[2].errors[\"sigma\"] ** 2,\n", " )\n", " )\n", " e_b_nll.append(\n", " (\n", " m[3].values[\"n\"] - n,\n", " m[3].errors[\"n\"] ** 2,\n", " m[3].values[\"mu\"] - truth.mu,\n", " m[3].errors[\"mu\"] ** 2,\n", " m[3].values[\"sigma\"] - truth.sigma,\n", " m[3].errors[\"sigma\"] ** 2,\n", " )\n", " )\n", "\n", " # means over pseudo-experiments are computed here\n", " return (\n", " np.mean(u_nll, axis=0),\n", " np.mean(e_u_nll, axis=0),\n", " np.mean(b_nll, axis=0),\n", " np.mean(e_b_nll, axis=0),\n", " )\n", "\n", "\n", "unbinned_nll = []\n", "extended_unbinned_nll = []\n", "binned_nll = []\n", "extended_binned_nll = []\n", "\n", "result = joblib.Parallel(-1)(compute(n) for n in n_pts)\n", "\n", "for a,b,c,d in result:\n", " unbinned_nll.append(a)\n", " extended_unbinned_nll.append(b)\n", " binned_nll.append(c)\n", " extended_binned_nll.append(d)\n", "\n", "unbinned_nll = np.transpose(unbinned_nll)\n", "extended_unbinned_nll = np.transpose(extended_unbinned_nll)\n", "binned_nll = np.transpose(binned_nll)\n", "extended_binned_nll = np.transpose(extended_binned_nll)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We plot the measured bias as a point and the mean variance as an error bar. The deviations go down with $n^{-{1/2}}$, where $n$ is the sample size. We undo this for the plots by multiplying deviations with $n^{1/2}$." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABG8AAALECAYAAAC2WB3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB3tElEQVR4nO3dd3hUZf738c+kk5ACMTQJXdlFShQEGxIWFOzo2hAV0GURQUVld2EtYNk9opFVkYeiKC4WYHFdFdtPUKyACCYSFSQKGggkQGQSEkg9zx8xI0MKKVPuTN6v65rrypz5zjn3zBzOfPnMKQ7btm0BAAAAAADASEH+HgAAAAAAAABqRngDAAAAAABgMMIbAAAAAAAAgxHeAAAAAAAAGIzwBgAAAAAAwGCENwAAAAAAAAYjvAEAAAAAADAY4Q0AAAAAAIDBCG8AAAAAAAAMRngDNDPJycnq3bv3cet27twph8OhJUuWeH9QjbBkyRI5HA7t3LnT30MBAAAByuFwaNasWf4eRq3Wrl0rh8OhtWvX+nsoALyA8AYw0KxZs+RwOLR///5qH+/du7eSk5N9O6gmrvI9bdu2rQoLC6s83qVLF1188cVu0xwOh6ZMmVLrfOsahgEAAHNU/vhz9K1NmzYaOnSo3nnnHX8Pz6sqX3tERIR2795d5fHqepvq+qRjjRs3Ti1btvToWAH8JsTfAwBgps6dO+vw4cMKDQ3191A8KicnR/Pnz9fdd9/t76EAAAA/e/DBB9W1a1fZtq3s7GwtWbJEF154od588023sOLw4cMKCQms/zoVFRXpkUce0dy5c/09FAB1wJ43AKpV+YtMcHCwv4fiUUlJSXrsscd0+PBhfw8FAAD42QUXXKDrr79eN9xwg6ZNm6ZPPvlEoaGheuWVV9zqIiIiAi68SUpK0jPPPKOsrCx/DwVAHRDeAAGg8hjnFStW6B//+Ic6duyoiIgIDRs2TBkZGdU+Z9OmTTrrrLPUokULde3aVQsWLHB7vLpz3lTuDrt7926NGjVKLVu2VEJCgqZNm6aysrIqz01JSdGiRYvUvXt3hYeH6/TTT9fGjRurjGXr1q268sor1bp1a0VERGjAgAF64403qtR98803+sMf/qAWLVqoY8eOevjhh1VeXl6v9+r+++9Xdna25s+fX6/nAQCAwBcXF6cWLVpUCWqOPedN5eHYGRkZGjdunOLi4hQbG6vx48dXOTy78jDs//3vf+rdu7fCw8N1yimn6N13362y/N27d+umm25S27ZtXXXPPfdclbpdu3Zp1KhRioqKUps2bXTnnXeqqKioXq/173//u8rKyvTII4/U63kA/COw4mOgmXvkkUcUFBSkadOmyel06tFHH9WYMWO0YcMGt7pffvlFF154oa6++mqNHj1aK1as0KRJkxQWFqabbrqp1mWUlZVpxIgRGjRokFJSUrR69Wo9/vjj6t69uyZNmuRW+/LLLys/P18TJ06Uw+HQo48+qiuuuEI//vij63Csb775RmeffbZOPPFETZ8+XVFRUVqxYoVGjRqlV199VZdffrkkae/evRo6dKhKS0tddYsWLVKLFi3q9R4NHjxYf/jDH/Too49q0qRJ9X4+AAAIHE6nU/v375dt28rJydHcuXN16NAhXX/99XV6/tVXX62uXbvKsixt3rxZzz77rNq0aaPZs2e71X366af673//q1tvvVXR0dF66qmn9Mc//lE///yz4uPjJUnZ2dk644wzXGFPQkKC3nnnHd18883Ky8vT1KlTJVUcwjVs2DD9/PPPuv3229WhQwctXbpUH3zwQb1ee9euXXXjjTfqmWee0fTp09WhQ4d6PR+AbxHeAAHkyJEjSk1NVVhYmCSpVatWuuOOO5Senu524rmsrCw9/vjjuuuuuyRJEydO1KBBgzRjxgzdcMMNtZ7n5siRI7rmmmt03333SZJuueUWnXbaaVq8eHGV8Obnn3/W9u3b1apVK0lSz549ddlll+m9995zHUd+xx13qFOnTtq4caPCw8MlSbfeeqvOOecc/e1vf3OFN7Nnz9a+ffu0YcMGDRw4UJI0duxYnXTSSfV+n2bOnKkhQ4ZowYIFuvPOO+v9fAAAEBiGDx/udj88PFzPPfeczjvvvDo9/9RTT9XixYtd9w8cOKDFixdXCW++++47ffvtt+revbskaejQoerXr59eeeUV18UR7rnnHpWVlWnLli2uQOeWW27R6NGjNWvWLE2cOFEtWrTQokWL9P3332vFihW66qqrJEkTJkxQv3796v3677nnHv373//W7Nmz9eSTT9b7+QB8h8OmgAAyfvx4V3AjVexlIkk//vijW11ISIgmTpzouh8WFqaJEycqJydHmzZtOu5ybrnlFrf7gwcPrrIMSbrmmmtcwU1148nNzdUHH3ygq6++Wvn5+dq/f7/279+vAwcOaMSIEdq+fbvrKghvv/22zjjjDFdwI0kJCQkaM2bMccd7rHPPPVdDhw7Vo48+yrlvAABoxubNm6f3339f77//vl588UUNHTpUf/rTn/Tf//63Ts+vric6cOCA8vLy3KYPHz7cFdxIUt++fRUTE+PqiWzb1quvvqpLLrlEtm27eqL9+/drxIgRcjqd2rx5s6SKnqh9+/a68sorXfOLjIzUn//853q//m7duumGG27QokWLtGfPnno/H4DvEN4ATZTD4agyrVOnTm73K4OTX375xW16hw4dFBUV5Tbt5JNPllRxvpraREREKCEhocpyjl1GXcaTkZEh27Z13333KSEhwe02c+ZMSRVXh5Kkn376qdq9bHr27FnreGsya9Ys7d27t8q5fgAAQPMxcOBADR8+XMOHD9eYMWP01ltvqVevXpoyZYqKi4uP+/y69l7H1lXWVtbt27dPBw8e1KJFi6r0ROPHj5fk3hP16NGjSi/Y0J7o3nvvVWlpKee+AQzHYVOAgSIiIiSpxr1CCgsLXTVHq+nKULZte2xs9bn61PHGU3my4WnTpmnEiBHV1vbo0aOeI6ybc889V8nJyXr00Uer/GoGAACap6CgIA0dOlRPPvmktm/frlNOOaXW+rr2XnXtia6//nqNHTu22tq+ffvWOpaG6tatm66//notWrRI06dP98oyADQe4Q1goM6dO0uStm3bpsTERLfHCgsLlZmZqfPPP7/B88/KylJBQYHb3jfff/+9JKlLly4Nnm99devWTZIUGhpa5ZjzY3Xu3Fnbt2+vMn3btm0NXv6sWbOUnJyshQsXNngeAAAgsJSWlkqSDh065LNlJiQkKDo6WmVlZXXqidLT02XbttveN43pie699169+OKLVc7VA8AcHDYFGGjYsGEKCwvT/Pnzq1wKe9GiRSotLdUFF1zQ4PmXlpa6BRbFxcVauHChEhIS1L9//wbPt77atGnjCk+qO8563759rr8vvPBCrV+/Xl988YXb4y+99FKDlz9kyBAlJydr9uzZOnLkSIPnAwAAAkNJSYn+7//+T2FhYfr973/vs+UGBwfrj3/8o1599VWlp6dXefzYnigrK0srV650TSssLNSiRYsavPzu3bvr+uuv18KFC7V3794GzweA97DnDWCgNm3a6P7779e9996rc889V5deeqkiIyP1+eef65VXXtH555+vSy65pMHz79Chg2bPnq2dO3fq5JNP1vLly5WamqpFixbVeqUpb5g3b57OOecc9enTRxMmTFC3bt2UnZ2tdevWadeuXUpLS5Mk/fWvf9XSpUs1cuRI3XHHHa5LhXfu3Flff/11g5c/c+ZMDR06tMbHv/zySz388MNVpicnJ+ucc86RVNFQVVfTtWvXBp1QGQAA+MY777yjrVu3Sqo4p8zLL7+s7du3a/r06YqJifHpWB555BF9+OGHGjRokCZMmKBevXopNzdXmzdv1urVq5Wbmyup4spSTz/9tG688UZt2rRJ7du319KlSxUZGdmo5d9zzz1aunSptm3bVu3hYhkZGdX2O6eeeqouuugiSRXhV3U1rVu31q233tqo8QHNHeENYKh77rlHXbp00dNPP60HH3xQpaWl6tq1qx544AH97W9/U1BQw3eca9WqlV544QXddttteuaZZ9S2bVs9/fTTmjBhggdfQd306tVLX375pR544AEtWbJEBw4cUJs2bXTqqafq/vvvd9W1b99eH374oW677TY98sgjio+P1y233KIOHTro5ptvbvDyk5OTNWTIEH300UfVPr5hwwZt2LChyvSHHnrIFd7k5OS4Lp1+tGHDhhHeAABgsKN7jYiICP3ud7/T/Pnz3a7K6Stt27bVF198oQcffFD//e9/9f/+3/9TfHy8TjnlFLfDmSIjI7VmzRrddtttmjt3riIjIzVmzBhdcMEFGjlyZIOX36NHD11//fV64YUXqn1827Zt1fY7N998syu8KS4urrame/fuhDdAIzlsT57JFAAAAAAAAB7FOW8AAAAAAAAMRngDAAAAAABgMMIbAAAAAAAAgxHeAAAAAAAAGIzwBgAAAAAAwGCENwAAAAAAAAYL8fcAalNeXq6srCxFR0fL4XD4ezgAAKAGtm0rPz9fHTp0UFAQvw35E/0TAABNQ336J6PDm6ysLCUmJvp7GAAAoI4yMzPVsWNHfw+jWaN/AgCgaalL/2R0eBMdHS2p4oXExMR4bsbFBdLjPSv+vnubFBbluXkDANAM5eXlKTEx0fXdDf/xWv8EAAA8qj79k9HhTeWuvjExMR4Ob4Kl8F93I46JIbwBAMBDOEzH/7zWPwEAAK+oS//EQekAAAAAAAAGI7wBAAAAAAAwGOENAAAA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pI0bN+q9995Tnz59dMUVV7hqn9SfVOiI1PgDuYqKrqhNTU3VqlWr1LNnT1177bWu2nnz5snpdOpPf/qTTjzxRElSenq6XnvtNXXr1k033HCDq/aZZ57Rvn37NHbsWHXp0kWS9P3332v58uVKTEzUTTfd5KpdsmSJsrKyNHr0aJ188smSpB07dujFF19U27Ztdcstt7hqX3rpJf3000+68sordcopp0iSdu3apeeff16tW7fWbbfd5qpdsWKFtm/frssuu0xJSUmSpJycHC1cuFDR0dG66667XLWvvfaavv32W11wwQUaOHCgJCk3N1dPP/20wsPDNX36dFftqlWrlJaWpuHDh+vss8+WJOXn5+tf//qXgoKCdN9997lq33vvPX355ZcaMmSIkpOTJUlFRUWaPXu2JOnee+91rcxr1qzRunXrdOaZZ+r888+XVPGPwbIsSdLf/vY3RURESJI++eQTffTRRxowYIAuuugi1/Jmz56t8vJy3XnnnYqJiZEkrV+/XqtXr1a/fv00atQoV+2cOXNUVFSkKVOmKD4+XpK0adMmvfPOO+rVq5euuuoqV+3cuXOVn5+viRMnql27dpKkLVu26PXXX9dJJ52k6667zlU7KvxbxQQVKSurj07u1lWS9N1332nlypXq3Lmzxo0b56pdvHixsrOzdf3116t79+6SpIyMDL3yyivq0KGDJkyY4KpdunSpMjMzdc011+h3v/udJOnnn3/WCy+8oISEBN16662u2mXLlunHH3/U5Zdfrr59+0qS9uzZo2effVaxsbGaOnWqq3blypXatm2bLr74YvXv31+StG/fPs2fP1+RkZH6y1/+4qp94403tGXLFo0YMUJnnHGGJMnpdOrJJ59UaGio/v73v7tq3377bX311VcaOnSozj33XElSYWGhUlJSJEkzZ8501a5evVobNmzQOeeco2HDhkmq2DhWfvYzZsxwbeDWrl2rTz/9VIMGDdLIkSNd86isnTZtmme3EU8+qcLCQk2aNElt2rSRxDaCbUTjthELFixQbm6uxo8fr06dOkkKrG3Exo0bBbN4rX96ah7bxmO3jcUFyv1nLz3tuIlto+q+bSwsLtV9/3hcMUFFGn3DjfRP9E+Bu40Q/RP9U+P7Jw6bAuBVhcWlOu2h9/09DAAAgCaD/gnAsRz20ccNGSYvL0+xsbHat2+f4uPj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P2VnZ+vBBx/U/fffrz//+c8aPHiwJOmss86SJH3wwQe64IIL1L9/f1eD8vzzz+sPf/iDPvnkEw0cONBt+VdffbW6du0qy7K0efNmPfvss2rTpo1mz57tqvnTn/6kF198Udddd53OOussffDBB7rooouqvL7s7GydccYZrnOhJCQk6J133tHNN9+svLw8TZ06VZJ0+PBhDRs2TD///LNuv/12dejQQUuXLtUHH3zgsfe6Oo888oiCgoI0bdo0OZ1OPfrooxozZow2bNjgVvfLL7/owgsv1NVXX63Ro0drxYoVmjRpksLCwnTTTTfVuoyysjKNGDFCgwYNUkpKilavXq3HH39c3bt316RJk9xqX375ZeXn52vixIlyOBx69NFHdcUVV+jHH390HY71zTff6Oyzz9aJJ56o6dOnKyoqSitWrNCoUaP06quv6vLLL5ck7d27V0OHDlVpaamrbtGiRfXee2bw4MH6wx/+oEcffVSTJk1i7xsAQJNCrzVVkud6rauvvlpdunSRZVlav369nnrqKf3yyy/VhivHqgxAZs6cqZ07d+qJJ57QlClTtHz5cre6jIwMXXnllbr55ps1duxYPffccxo3bpz69++vU045RZJUWFioIUOGaPfu3Zo4caI6deqkzz//XDNmzNCePXv0xBNPSJJs29Zll12mTz/9VLfccot+//vf67XXXtPYsWPr9bpjYmJ055136v7772fvG8AGmpHnn3/ellTtLTw83K12y5YtdlhYmP2nP/3J/uWXX+wTTzzRHjBggF1SUuKq2bhxoy3Jfv75592eW15ebp900kn2iBEj7PLyctf0wsJCu2vXrvZ5553nmjZz5kxbkn3TTTe5zePyyy+34+PjXfdTU1NtSfatt97qVnfdddfZkuyZM2e6pt188812+/bt7f3797vVXnvttXZsbKxdWFho27ZtP/HEE7Yke8WKFa6agoICu0ePHrYk+8MPP6zl3fxt7Pv27av28VNOOcUeMmSI6/6HH35oS7J///vf20VFRa7pTz75pC3J3rJli2vakCFDbEn2448/7ppWVFRkJyUl2W3atLGLi4tt27btHTt2VPkMxo4da0uyH3zwQbfxnHrqqXb//v1d9yufGx8fb+fm5rqmv/7667Yk+80333RNGzZsmN2nTx/7yJEjrmnl5eX2WWedZZ900kmuaVOnTrUl2Rs2bHBNy8nJsWNjY21J9o4dO6p9ryod/Z5+9NFHtiR7zpw5rsc7d+5sX3TRRW7PkWRPnjy51vkOGTLEPuWUU2qtAQCgsei1vNNrXXrppW7Tb731VluSnZaW5prWuXNne+zYsa77lZ/F8OHD3d6jO++80w4ODrYPHjzo9lxJ9scff+yalpOTY4eHh9t33323a9pDDz1kR0VF2d9//73beKZPn24HBwfbP//8s23btv2///3PlmQ/+uijrprS0lJ78ODB1X6ex6rsGf/zn//YBw8etFu1auX2HowdO9aOiopye05dep3j9a6AyThsCs3SvHnz9P7777vd3nnnHbea3r1764EHHtCzzz6rESNGaP/+/XrhhRcUEnL8HdZSU1O1fft2XXfddTpw4ID279+v/fv3q6CgQMOGDdPHH39cZdfSW265xe3+4MGDdeDAAeXl5UmS3n77bUnS7bff7lZX+ctOJdu29eqrr+qSSy6RbduuZe/fv18jRoyQ0+nU5s2bXfNs3769rrzyStfzIyMj9ec///m4r7Exxo8fr7CwMNf9yl/SfvzxR7e6kJAQTZw40XU/LCxMEydOVE5OTrVXHjhWde/pscuQpGuuuUatWrWqcTy5ubn64IMPdPXVVys/P9/1fh44cEAjRozQ9u3btXv3bkkV7+kZZ5zh9mtfQkKCxowZc9zxHuvcc8/V0KFD9eijj+rw4cP1fj4AAP5Cr+XZXmvy5Mlu92+77Ta3Mdfmz3/+sxwOh+v+4MGDVVZWpp9++smtrlevXq4eSKroX3r27OnWO/3nP//R4MGD1apVK7fXPXz4cJWVlenjjz92jSskJMRtb+fg4GDXuOsjNjZWU6dO1RtvvKGvvvqq3s8HAgWHTaFZGjhwYJ1OoveXv/xFy5Yt0xdffKF//vOf6tWrV53mv337dkmqdddQp9PpFhh06tTJ7fHKx3755RfFxMTop59+UlBQkLp37+5W17NnT7f7+/bt08GDB7Vo0SItWrSo2mXn5ORIkn766Sf16NHD7Qu9unk2xrHzlmp/rUfr0KGDoqKi3KadfPLJkirOV3PGGWfUuNyIiAglJCRUWc6xy6jLeDIyMmTbtu677z7dd9991S4vJydHJ554on766ScNGjSoyuMNfU9nzZqlIUOGaMGCBbrzzjsbNA8AAHyNXsuzvdZJJ53kdr979+4KCgrSzp07j/vcuvZdx9ZV1h5dt337dn399ddVeqxKR7/u9u3bV7mcd0P7oTvuuEP/+te/NGvWLL3++usNmgfQ1BHeALX48ccfXc3Bli1b6vy8yl96HnvssRova3nsl1lwcHC1dbZt13m5Ry/7+uuvr7Gh6du3b73mWZPKqzzVtFdIYWFhtVeC8tRrrU1Ny6hPbeV4Kt/TadOmacSIEdXW1vfSp3V17rnnKjk5WY8++miVXwwBAGjq6LUaprofx2pS19ddl7ry8nKdd955+utf/1ptbeWPbJ5WuffNrFmz2PsGzRbhDVCD8vJyjRs3TjExMZo6dar++c9/6sorr9QVV1zhqqnpi7PyF5uYmBgNHz7cI+Pp3LmzysvL9cMPP7j9arFt2za3usqrI5SVlR132Z07d1Z6erps23Z7LcfOs7bnV9YnJia6PVZYWKjMzEydf/75dZpXdbKyslRQUOC29833338vqeKqCL7SrVs3SVJoaGid3tPKJvRodX1PqzNr1iwlJydr4cKFDZ4HAACmodequ+3bt6tr166u+xkZGSovL/dpPyRVvO+HDh2q0+tes2aNDh065BaiNaYfmjp1qp544gk98MADiouLa/B8gKaKc94ANZgzZ44+//xzLVq0SA899JDOOussTZo0ye3KCZWhwrGXPezfv7+6d++ulJQUHTp0qMq8j708Y11ccMEFkqSnnnrKbXrlWf0rBQcH649//KNeffVVpaen17rsCy+8UFlZWVq5cqVrWmFhYY27AB9r2LBhCgsL0/z586scV75o0SKVlpa6xt0QpaWlboFFcXGxFi5cqISEBPXv37/B862vNm3auMKTPXv2VHn82Pd0/fr1+uKLL9wef+mllxq8/CFDhig5OVmzZ8+ucllQAACaKnqtups3b57b/blz57qN2VeuvvpqrVu3Tu+9916Vxw4ePKjS0lJJFa+7tLRU8+fPdz1eVlbmGndDVO598/rrrys1NbXB8wGaKva8QbP0zjvvaOvWrVWmn3XWWerWrZu+++473XfffRo3bpwuueQSSdKSJUuUlJSkW2+9VStWrJBU8etDXFycFixYoOjoaEVFRWnQoEHq2rWrnn32WV1wwQU65ZRTNH78eJ144onavXu3PvzwQ8XExOjNN9+s15iTkpI0evRo/b//9//kdDp11llnac2aNcrIyKhS+8gjj+jDDz/U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9lpX7bx58+R0OvWnP/1JJ554oiQpPT1dr732mrp166YbbrjBVfvMM89o3759Gjt2rLp06SJJ+v7777V8+XIlJibqpptuctUuWbJEWVlZGj16tE4++WRJ0o4dO/Tiiy+qbdu2uuWWW1y1L730kn766SddeeWVOuWUUyRJu3bt0vPPP6/WrVvrtttuc9WuWLFC27dv12WXXaakpCRJUk5OjhYuXKjo6GjdddddrtrXXntN3377rS644AINHDhQkpSbm6unn35a4eHhmj59uqt21apVSktL0/Dhw3X22WdLkvLz8/Wvf/1LQUFBuu+++1y17733nr788ksNGTJEycnJkqSioiLNnj1bknTvvfe6VuY1a9Zo3bp1OvPMM3X++edLqvjHYFmWJOlvf/ubIiIiJEmffPKJPvroIw0YMEAXXXSRa3mzZ89WeXm57rzzTsXExEiS1q9fr9WrV6tfv34aNWqUq3bOnDkqKirSlClTFB8fL0natGmT3nnnHfXq1UtXXXWVq3bu3LnKz8/XxIkT1a5dO0nSli1b9Prrr+ukk07Sdddd56odFf6tYoKKlJXVRyd36ypJ+u6777Ry5Up17txZ48aNc9UuXrxY2dnZuv7669W9e3dJUkZGhl555RV16NBBEyZMcNUuXbpUmZmZuuaaa/S73/1OkvTzzz/rhRdeUEJCgm699VZX7bJly/Tjjz/q8ssvV9++fSVJe/bs0bPPPqvY2FhNnTrVVbty5Upt27ZNF198sfr37y9J2rdvn+bPn6/IyEj95S9/cdW+8cYb2rJli0aMGKEzzjhDkuR0OvXkk08qNDRUf//73121b7/9tr766isNHTpU5557riSpsLBQKSkpkqSZM2e6alevXq0NGzbonHPO0bBhwyRVbBwrP/sZM2a4NnBr167Vp59+qkGDBmnkyJGueVTWTps2zbPbiCefVGFhoSZNmqQ2bdpIYhvBNqJx24gFCxYoNzdX48ePV6dOnSQF1jZi48aNglm81j89NY9t47HbxuIC5f6zl5523MS2UXXfNhYWl+q+fzyumKAijb7hRvon+qfA3UaI/on+qfH9E4dNAfCqwuJSnfbQ+/4eBgAAQJNB/wTgWA776OOGDJOXl6fY2Fjt27dP8fHxHttVq/CQUyEp3SRJJXf/4NrzJpB31WKXPg6b8tdnX1hcql73v6sQlevLe4cr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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(2, 2, figsize=(14, 8), sharex=True, sharey=True)\n", "\n", "plt.sca(ax[0, 0])\n", "plt.title(\"Unbinned NLL\")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * unbinned_nll[0],\n", " np.sqrt(n_pts * unbinned_nll[1]),\n", " fmt=\"o\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\mu$\",\n", ")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * unbinned_nll[2],\n", " np.sqrt(n_pts * unbinned_nll[3]),\n", " fmt=\"s\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\sigma$\",\n", ")\n", "\n", "plt.sca(ax[0, 1])\n", "plt.title(\"Binned NLL\")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * binned_nll[0],\n", " np.sqrt(n_pts * binned_nll[1]),\n", " fmt=\"o\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\mu$\",\n", ")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * binned_nll[2],\n", " np.sqrt(n_pts * binned_nll[3]),\n", " fmt=\"s\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\sigma$\",\n", ")\n", "\n", "plt.sca(ax[1, 0])\n", "plt.title(\"Extended Unbinned NLL\")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * extended_unbinned_nll[2],\n", " np.sqrt(n_pts * extended_unbinned_nll[3]),\n", " fmt=\"o\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\mu$\",\n", ")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * extended_unbinned_nll[4],\n", " np.sqrt(n_pts * extended_unbinned_nll[5]),\n", " fmt=\"s\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\sigma$\",\n", ")\n", "\n", "plt.sca(ax[1, 1])\n", "plt.title(\"Extended binned NLL\")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * extended_binned_nll[2],\n", " np.sqrt(n_pts * extended_binned_nll[3]),\n", " fmt=\"o\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\mu$\",\n", ")\n", "plt.errorbar(\n", " n_pts,\n", " n_pts ** 0.5 * extended_binned_nll[4],\n", " np.sqrt(n_pts * extended_binned_nll[5]),\n", " fmt=\"s\",\n", " label=r\"$\\sqrt{n}\\,\\Delta\\sigma$\",\n", ")\n", "\n", "plt.ylim(-5, 5)\n", "plt.legend()\n", "plt.semilogx();\n", "for i in (0, 1):\n", " ax[1, i].set_xlabel(r\"$n_\\mathrm{pts}$\")\n", "for axi in ax.flat:\n", " for y in (-1, 1):\n", " axi.axhline(y, ls=\":\", color=\"0.5\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Least-squares fits\n", "\n", "We do the same as before, but this time we use a least-squares fit of $x,y$ scattered data and vary the residual function. Other functions than the identity can be used to reduce the pull of large outliers, turning the ordinary least-squares fit into a robust fit." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10000 17 need to re-try [(True, True), (True, True), (False, False)]\n", "10000 69 need to re-try [(True, True), (True, True), (False, False)]\n" ] } ], "source": [ "n_tries = 100 # increase this to 500 to get less scattering\n", "\n", "truth = Namespace(a=1, b=2)\n", "\n", "n_pts = np.array((10, 30, 100, 300, 1000, 3000, 10000))\n", "\n", "\n", "@joblib.delayed\n", "def compute(n):\n", " rng = np.random.default_rng(n)\n", " x = np.linspace(0, 1, n)\n", "\n", " linear = []\n", " soft_l1 = []\n", " arctan = []\n", " for i_try in range(n_tries):\n", "\n", " def model(x, a, b):\n", " return a + b * x\n", "\n", " while True:\n", " y = model(x, 1, 2)\n", " ye = 0.1\n", " y += rng.normal(0, ye, len(y))\n", "\n", " m = [\n", " Minuit(LeastSquares(x, y, ye, model), a=0, b=0),\n", " Minuit(LeastSquares(x, y, ye, model, loss=\"soft_l1\"), a=0, b=0),\n", " Minuit(LeastSquares(x, y, ye, model, loss=np.arctan), a=0, b=0),\n", " ]\n", "\n", " all_good = True\n", " for mi in m:\n", " mi.migrad()\n", " mi.hesse()\n", " if not mi.valid or not mi.accurate:\n", " all_good = False\n", " break\n", " if all_good:\n", " break\n", " print(f\"{n} {i_try} need to re-try {[(mi.valid, mi.accurate) for mi in m]}\")\n", "\n", " linear.append(\n", " (\n", " m[0].values[\"a\"] - truth.a,\n", " m[0].values[\"b\"] - truth.b,\n", " m[0].errors[\"a\"] ** 2,\n", " m[0].errors[\"b\"] ** 2,\n", " )\n", " )\n", " soft_l1.append(\n", " (\n", " m[1].values[\"a\"] - truth.a,\n", " m[1].values[\"b\"] - truth.b,\n", " m[1].errors[\"a\"] ** 2,\n", " m[1].errors[\"b\"] ** 2,\n", " )\n", " )\n", " arctan.append(\n", " (\n", " m[2].values[\"a\"] - truth.a,\n", " m[2].values[\"b\"] - truth.b,\n", " m[2].errors[\"a\"] ** 2,\n", " m[2].errors[\"b\"] ** 2,\n", " )\n", " )\n", "\n", " return [\n", " (*np.mean(t, axis=0), *np.var(np.array(t)[:,:2], axis=0))\n", " for t in (linear, soft_l1, arctan)\n", " ]\n", "\n", "linear = []\n", "soft_l1 = []\n", "arctan = []\n", "\n", "for l, s, a in joblib.Parallel(-1)(compute(n) for n in n_pts):\n", " linear.append(l)\n", " soft_l1.append(s)\n", " arctan.append(a)\n", "\n", "linear = np.transpose(linear)\n", "soft_l1 = np.transpose(soft_l1)\n", "arctan = np.transpose(arctan)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/png": "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oXr37q1LLrlEzz77rL799ls999xzatSokUaNGlXma3j06FFdccUVio2N1YQJExQTE6N9+/bp008/tfUxDEP9+vXTihUrNHz4cLVu3VpLlizR/fffr0OHDumFF15QbGys3nvvPT3++OM6deqUUlNTJUlNmjRxentypxtvvFHJyclKTU3V5s2b9eabbyouLs72tyFJI0aM0Lvvvqvrr79e48eP1/r165Wamqpff/1Vn332md3ydu3apUGDBun//u//NHLkSDVt2tT2WGpqqiIiIjRhwgTt3r1bL7/8skJCQhQUFKSTJ09q6tSpWrdunWbPnq3k5GRNnjzZba8DvBf7gLKxD6j8PkAqKhpWq1ZN48aNU7Vq1bR8+XJNnjxZWVlZeuaZZ+z6/vHHH7ryyit1880365ZbblHt2rVVWFioq6++WsuWLdPNN9+sMWPG6M8//9TSpUu1bds29erVy2H8S5cu1X//+18NGzZM8fHx2r59u2bNmqXt27dr3bp1slgsdjGUJ2+X5eTJk+rTp4+uu+463XjjjZo/f74efPBBtWjRQldeeaWkom21e/fu2r17t+666y4lJyfr448/1tChQ5WRkaExY8bYLbO07arYTTfdpAsvvFBPPvmkvvzySz322GOqWbOmXn/9dfXo0UNPPfWU3n//fd133326+OKL1bVr1/Oug8cYgA975513DEnGjz/+WOrjw4cPN+rUqWMcP37crv3mm282oqOjjZycHMMwDKOgoMDIzc2163Py5Emjdu3axu23325rGzNmjBEVFWUUFBSUGdPHH39sSDJWrFhRrnV44YUXDEnGsWPHyuwzY8YMQ5Ixb948W1t2drbRuHHjEmMlJSUZt912W4lldOvWzejWrZvtfnnXee/evYYkIyoqyjh69Khd/549exotWrQwzpw5Y2uzWq1Gp06djCZNmtjaWrVqZfTt27fM9QMC3flymWGQz87mi/ls9OjRRu3atW33x40bZ3Tt2tWIi4szXnvtNcMwDOOPP/4wLBaL8eKLL9r63XbbbUZSUpLt/rFjxwxJxpQpU0qMcdtttxmSjOnTp9u1t2nTxmjbtq3D+D777LPzboOff/65Icl47LHH7Nqvv/56w2KxGLt377a1devWzfj73/9u18/Z7WnKlCnn3Z7O5ui1Od8YZ28nhmEY1157rXHBBRfY7m/ZssWQZIwY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u+++q3/961+69tpr1ahRI/3555964403FBUVZSueXHPNNbrsssv00EMPad++fWrVqpW++eYbLViwQGPHji3xvpzL2e2p2PPPP19i+wgKCtKkSZMkSe+9957279+vnJwcSdKqVav02GOPSZJuvfVWl3xL3KpVK912222aNWuW7VSPDRs26N1339WAAQN02WWXVXoMgH2APfYBJZm1D+jUqZNq1Kih2267Tffcc48sFovee+89p06nGjJkiObMmaNx48Zpw4YN6tKli7Kzs/Xtt9/qn//8p/r37+8w/q5du+rpp59Wfn6+EhMT9c033zh1BJSr3XHHHXr99dc1dOhQbdq0SQ0aNND8+fP1ww8/aMaMGSXmAwwYJl/lDTBV8WVPy7odPHjQSE9PN0aPHm3Uq1fPCAkJMeLj442ePXsas2bNsi3HarUaTzzxhJGUlGSEhYUZbdq0MRYtWlTi0pnz5883rrjiCiMuLs4IDQ016tevb/zf//2fceTIEbu43njjDaNhw4ZGcHDweS9/umzZMqN///5GQkKCERoaaiQkJBiDBg0y/vOf/9j1279/v9GvXz8jMjLSqFWrljFmzBhj8eLFpS7/ueeeMxITE42wsDDj0ksvNTZu3FjikqflXefiS1M+88wzpca/Z88eY8iQIUZ8fLwREhJiJCYmGldffbUxf/58W5/HHnvMaN++vRETE2NEREQYzZo1Mx5//HG7yxgDgaw8ucwwDPKZH+SzuLg4Q5KRnp5ua1u9erUhyejSpUuJ/ueug2EYxpo1a4y2bdsaoaGhdpdDvu2224yqVauWWEbxZYQd2bx5szFo0CCjfv36RlhYmBEXF2dcffXVxsaNG+36/fnnn8a9995rJCQkGCEhIUaTJk2MZ555xnaJ5mLdunUz/v73v5cYx5ntqTju0m7BwcF2Y5XV73yXHy8e49zLjhf/Te7du9fWlp+fb0ybNs1ITk42QkJCjHr16hkTJ060u7y1YRRdery0y3yvWLHCkGR8/PHHpY517iXcy4oN/od9APsAT+8DDMMwfvjhB6NDhw5GRESEkZCQYDzwwAPGkiVLSrwvZeV3wzCMnJwc46GHHrLlyfj4eOP666839uzZc974f//9d+Paa681YmJijOjoaOOGG24wDh8+bNfn7PUpT94uTVnxl/Zap6enG8OGDTNq1aplhIaGGi1atDDe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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(2, 3, figsize=(14, 8), sharex=True, sharey=False)\n", "\n", "for k, (title, func) in enumerate((\n", " (\"Least-squares\", linear),\n", " (\"Least-squares with soft L1 norm\", soft_l1),\n", " (\"Least-squares with arctan norm\", arctan),\n", ")):\n", " ax[0, k].set_title(title)\n", " for i, x in enumerate(\"ab\"):\n", " ax[0, k].errorbar(\n", " n_pts * 0.95 + 0.1 * i,\n", " np.sqrt(n_pts) * func[0 + i],\n", " np.sqrt(n_pts * func[4 + i]),\n", " fmt=\"so\"[i],\n", " label=f\"$\\sqrt{{n}}\\,\\Delta {x}$\",\n", " )\n", " ax[1, k].plot(\n", " n_pts * 0.95 + 0.1 * i,\n", " func[2 + i] / func[4 + i],\n", " \"so\"[i],\n", " label=f\"$\\sqrt{{n}}\\,\\Delta {x}$\",\n", " )\n", " ax[0, k].legend()\n", "plt.semilogx()\n", "for i in range(3):\n", " ax[1, i].axhline(1, ls=\"--\", color=\"0.5\")\n", " ax[0, i].set_ylim(-2, 2)\n", " ax[1, i].set_ylim(0.7, 3)\n", "ax[0, 0].set_ylabel(\"bias and variance\")\n", "ax[1, 0].set_ylabel(\"estimated variance / true variance\")\n", "fig.supxlabel(r\"$n_\\mathrm{pts}$\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The normal least-squares fit has a smallest variance, which is equal to the minimum variance for this problem given by the Cramer-Rao bound. The robust fits use less information to achieve robustness, hence the variance is larger. The loss from the soft L1 norm in this case is nearly negligible, but for the arctan norm it is noticable.\n", "\n", "**Beware**: The variance estimate obtained from the fit is wrong for robust least-squares, since the robust least-squares is not even asymptotically a maximum-likelihood estimator. The estimate is significantly larger than the actual variance for the soft_l1 and arctan norms in this case." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/cost_functions.ipynb0000644000000000000000000006727114332717401017075 0ustar00{ "cells": [ { "cell_type": "markdown", "id": "negative-concord", "metadata": {}, "source": [ "# Cost functions\n", "\n", "We give an in-depth guide on how to use the builtin cost functions.\n", "\n", "The iminuit package comes with a couple of common cost functions that you can import from `iminuit.cost` for convenience. Of course, you can write your own cost functions to use with iminuit, but most of the cost function is always the same. What really varies is the statistical model which predicts the probability density as a function of the parameter values. This you still have to provide yourself and the iminuit package will not include machinery to build statistical models (that is out of scope).\n", "\n", "Using the builtin cost functions is not only convenient, they also have some extra features.\n", "\n", "* Support of fitted weighted histograms.\n", "* Technical tricks improve numerical stability.\n", "* Optional numba acceleration (if numba is installed).\n", "* Cost functions can be added to fit data sets with shared parameters.\n", "* Temporarily mask data.\n", "\n", "We demonstrate each cost function on an standard example from high-energy physics, the fit of a peak over some smooth background (here taken to be constant)." ] }, { "cell_type": "code", "execution_count": null, "id": "lucky-canvas", "metadata": {}, "outputs": [], "source": [ "from iminuit import cost, Minuit\n", "# faster than scipy.stats functions\n", "from numba_stats import truncnorm, truncexpon, norm, expon \n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "from scipy.stats import multivariate_normal as mvnorm" ] }, { "cell_type": "markdown", "id": "absent-missile", "metadata": {}, "source": [ "We generate our data. We sample from a Gaussian peak and from exponential background in the range 0 to 2. We then bin the original data. One can fit the original or the binned data." ] }, { "cell_type": "code", "execution_count": null, "id": "destroyed-fusion", "metadata": {}, "outputs": [], "source": [ "xr = (0, 2) # xrange\n", "\n", "rng = np.random.default_rng(1)\n", "\n", "xdata = rng.normal(1, 0.1, size=1000)\n", "ydata = rng.exponential(size=len(xdata))\n", "xmix = np.append(xdata, ydata)\n", "xmix = xmix[(xr[0] < xmix) & (xmix < xr[1])]\n", "\n", "n, xe = np.histogram(xmix, bins=20, range=xr)\n", "cx = 0.5 * (xe[1:] + xe[:-1])\n", "dx = np.diff(xe)\n", "\n", "plt.errorbar(cx, n, n ** 0.5, fmt=\"ok\")\n", "plt.plot(xmix, np.zeros_like(xmix), \"|\", alpha=0.1);" ] }, { "cell_type": "markdown", "id": "5c50cab3", "metadata": {}, "source": [ "We also generate some 2D data to demonstrate multivariate fits. In this case, a gaussian along axis 1 and independently an exponential along axis 2. In this case, the distributions are not restricted to some range in x and y." ] }, { "cell_type": "code", "execution_count": null, "id": "b62cbb46", "metadata": {}, "outputs": [], "source": [ "n2, _, ye = np.histogram2d(xdata, ydata, bins=(20, 5), range=(xr, (0, np.max(ydata))))\n", "\n", "plt.pcolormesh(xe, ye, n2.T)\n", "plt.scatter(xdata, ydata, marker=\".\", color=\"w\", s=1);" ] }, { "cell_type": "markdown", "id": "interesting-cursor", "metadata": {}, "source": [ "## Maximum-likelihood fits\n", "\n", "Maximum-likelihood fits are the state-of-the-art when it comes to fitting models to data. The can be applied to unbinned and binned data (histograms).\n", "\n", "* Unbinned fits are the easiest to use, because they can be apply directly to the raw sample. They become slow when the sample size is large.\n", "* Binned fits require you to appropriately bin the data. The binning has to be fine enough to retain all essential information. Binned fits are much faster when the sample size is large.\n", "\n", "### Unbinned fit\n", "\n", "Unbinned fits are ideal when the data samples are not too large or very high dimensional. There is no need to worry about the appropriate binning of the data. Unbinned fits are inefficient when the samples are very large and can become numerically unstable, too. Binned fits are a better choice then.\n", "\n", "The cost function for an unbinned maximum-likelihood fit is really simple, it is the sum of the logarithm of the pdf evaluated at each sample point (times -1 to turn maximimization into minimization). You can easily write this yourself, but a naive implementation will suffer from instabilities when the pdf becomes locally zero. Our implementation mitigates the instabilities to some extend.\n", "\n", "To perform the unbinned fit you need to provide the pdf of the model, which must be vectorized (a numpy ufunc). The pdf must be normalized, which means that the integral over the sample value range must be a constant for any combination of model parameters.\n", "\n", "The model pdf in this case is a linear combination of the normal and the exponential pdfs. The parameters are $z$ (the weight), $\\mu$ and $\\sigma$ of the normal distribution and $\\tau$ of the exponential. The cost function detects the parameter names.\n", "\n", "It is important to put appropriate limits on the parameters, so that the problem does not become mathematically undefined.\n", "* $0 < z < 1$,\n", "* $\\sigma > 0$,\n", "* $\\tau > 0$.\n", "\n", "In addition, it can be beneficial to use $-1 < \\mu < 1$ (optional), but it is not required. We use `truncnorm` and `truncexpon`, which are normalised inside the data range (0, 2)." ] }, { "cell_type": "code", "execution_count": null, "id": "uniform-drama", "metadata": {}, "outputs": [], "source": [ "def pdf(x, z, mu, sigma, tau):\n", " return (z * truncnorm.pdf(x, *xr, mu, sigma) + \n", " (1 - z) * truncexpon.pdf(x, *xr, 0.0, tau))\n", "\n", "c = cost.UnbinnedNLL(xmix, pdf)\n", "\n", "m = Minuit(c, z=0.4, mu=0.1, sigma=0.2, tau=2)\n", "m.limits[\"z\"] = (0, 1)\n", "m.limits[\"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "380b6ca6", "metadata": {}, "source": [ "We can also fit a multivariate model to multivariate data. We pass model as a logpdf this time, which works well because the pdfs factorise." ] }, { "cell_type": "code", "execution_count": null, "id": "9da33a94", "metadata": {}, "outputs": [], "source": [ "def logpdf(xy, mu, sigma, tau):\n", " x, y = xy\n", " return (norm.logpdf(x, mu, sigma) + expon.logpdf(y, 0, tau))\n", "\n", "c = cost.UnbinnedNLL((xdata, ydata), logpdf, log=True)\n", "m = Minuit(c, mu=1, sigma=2, tau=2)\n", "m.limits[\"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "introductory-watershed", "metadata": {}, "source": [ "### Extended unbinned fit\n", "\n", "An important variant of the unbinned ML fit is described by [Roger Barlow, Nucl.Instrum.Meth.A 297 (1990) 496-506](https://inspirehep.net/literature/297773). Use this if both the shape and the integral of the density are of interest. In practice, this is often the case, for example, if you want to estimate a cross-section or yield.\n", "\n", "The model in this case has to return the integral of the density and the density itself (which must be vectorized). The parameters in this case are those already discussed in the previous section and in addition $s$ (integral of the signal density), $b$ (integral of the uniform density). The additional limits are:\n", "\n", "* $s > 0$,\n", "* $b > 0$.\n", "\n", "Compared to the previous case, we have one more parameter to fit." ] }, { "cell_type": "code", "execution_count": null, "id": "expanded-japanese", "metadata": {}, "outputs": [], "source": [ "def density(x, s, b, mu, sigma, tau):\n", " return s + b, (s * truncnorm.pdf(x, *xr, mu, sigma) + \n", " b * truncexpon.pdf(x, *xr, 0, tau))\n", "\n", "c = cost.ExtendedUnbinnedNLL(xmix, density)\n", "\n", "m = Minuit(c, s=300, b=1500, mu=0, sigma=0.2, tau=2)\n", "m.limits[\"s\", \"b\", \"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "understood-monte", "metadata": {}, "source": [ "The fitted values and the uncertainty estimates for the shape parameters are identical to the previous fit." ] }, { "cell_type": "code", "execution_count": null, "id": "governmental-hardware", "metadata": {}, "outputs": [], "source": [ "m.visualize()" ] }, { "cell_type": "markdown", "id": "262567e0", "metadata": {}, "source": [ "Once again, we fit 2D data, using the logdensity mode." ] }, { "cell_type": "code", "execution_count": null, "id": "68ba4583", "metadata": {}, "outputs": [], "source": [ "def logdensity(xy, n, mu, sigma, tau):\n", " x, y = xy\n", " return n, np.log(n) + norm.logpdf(x, mu, sigma) + expon.logpdf(y, 0, tau)\n", "\n", "c = cost.ExtendedUnbinnedNLL((xdata, ydata), logdensity, log=True)\n", "m = Minuit(c, n=1, mu=1, sigma=2, tau=2)\n", "m.limits[\"n\", \"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "controlling-celebration", "metadata": {}, "source": [ "### Binned Fit\n", "\n", "Binned fits are computationally more efficient and numerically more stable when samples are large. The caveat is that one has to choose an appropriate binning. The binning should be fine enough so that the essential information in the original is retained. Using large bins does not introduce a bias, but the parameters have a larger-than-minimal variance.\n", "\n", "In this case, 50 bins are fine enough to retain all information. Using a large number of bins is safe, since the maximum-likelihood method correctly takes poisson statistics into account, which works even if bins have zero entries. Using more bins than necessary just increases the computational cost.\n", "\n", "Instead of a pdf, you need to provide a cdf for a binned fit (which must be vectorized). Note that you can approximate the cdf as \"bin-width times pdf evaluated at center\", if the cdf is expensive to calculate, but this is an approxmiation and will lead to a bias. Using the cdf avoids this bias." ] }, { "cell_type": "code", "execution_count": null, "id": "robust-groove", "metadata": {}, "outputs": [], "source": [ "def cdf(xe, z, mu, sigma, tau):\n", " return (z * truncnorm.cdf(xe, *xr, mu, sigma) + \n", " (1-z) * truncexpon.cdf(xe, *xr, 0, tau))\n", "\n", "c = cost.BinnedNLL(n, xe, cdf)\n", "m = Minuit(c, z=0.4, mu=0, sigma=0.2, tau=2)\n", "m.limits[\"z\"] = (0, 1)\n", "m.limits[\"sigma\", \"tau\"] = (0.01, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "comparable-special", "metadata": {}, "source": [ "The fitted values and the uncertainty estimates for $\\mu$ and $\\sigma$ are not identical to the unbinned fit, but very close. For practical purposes, the results are equivalent. This shows that the binning is fine enough to retain the essential information in the original data.\n", "\n", "Note that iminuit also shows the chi2/ndof goodness-of-fit estimator when the data are binned. It can be calculated for free in the binned case." ] }, { "cell_type": "markdown", "id": "c7a06b88", "metadata": {}, "source": [ "Fitting a multidimensional histogram is equally easy. Since the pdfs in this example factorise, the cdf of the 2D model is the product of the cdfs along each axis." ] }, { "cell_type": "code", "execution_count": null, "id": "fad40fc9", "metadata": {}, "outputs": [], "source": [ "def cdf(xe_ye, mu, sigma, tau):\n", " xe, ye = xe_ye\n", " return norm.cdf(xe, mu, sigma) * expon.cdf(ye, 0, tau)\n", "\n", "c = cost.BinnedNLL(n2, (xe, ye), cdf)\n", "m = Minuit(c, mu=0.1, sigma=0.2, tau=2)\n", "m.limits[\"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "id": "a6c8ae4e", "metadata": {}, "source": [ "The automatically provided visualization for multi-dimensional data set is often not very pretty, but still helps to judge whether the fit is reasonable. There is no obvious way to draw higher dimensional data with error bars in comparison to a model, and so the automatic visualization shows all data bins as a single sequence. You can override the default visualization by assigning a plot function to the cost function `BinnedNLL` (monkey patching), by deriving your own class from `BinnedNLL`, or by calling `Minuit.visualize` with your own plotting function." ] }, { "cell_type": "markdown", "id": "decent-treat", "metadata": {}, "source": [ "### Extended binned maximum-likelihood fit\n", "\n", "As in the unbinned case, the binned extended maximum-likelihood fit should be used when also the amplitudes of the pdfs are of interest.\n", "\n", "Instead of a density, you need to provide the integrated density in this case (which must be vectorized). There is no need to separately return the total integral of the density, like in the unbinned case. The parameters are the same as in the unbinned extended fit." ] }, { "cell_type": "code", "execution_count": null, "id": "suitable-fetish", "metadata": {}, "outputs": [], "source": [ "def integral(xe, s, b, mu, sigma, tau):\n", " return (s * truncnorm.cdf(xe, *xr, mu, sigma) +\n", " b * truncexpon.cdf(xe, *xr, 0, tau))\n", "\n", "c = cost.ExtendedBinnedNLL(n, xe, integral)\n", "m = Minuit(c, s=300, b=1500, mu=0, sigma=0.2, tau=2)\n", "m.limits[\"s\", \"b\", \"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "noticed-wireless", "metadata": {}, "source": [ "Again, we can also fit multivariate data." ] }, { "cell_type": "code", "execution_count": null, "id": "aeb53009", "metadata": {}, "outputs": [], "source": [ "def integral(xe_ye, n, mu, sigma, tau):\n", " xe, ye = xe_ye\n", " return n * norm.cdf(xe, mu, sigma) * expon.cdf(ye, 0, tau)\n", "\n", "c = cost.ExtendedBinnedNLL(n2, (xe, ye), integral)\n", "m = Minuit(c, n=1500, mu=0.1, sigma=0.2, tau=2)\n", "m.limits[\"n\", \"sigma\", \"tau\"] = (0, None)\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "infectious-trash", "metadata": {}, "source": [ "### Temporary masking\n", "\n", "In complicated binned fits with peak and background, it is sometimes useful to fit in several stages. One typically starts by masking the signal region, to fit only the background region.\n", "\n", "The cost functions have a mask attribute to that end. We demonstrate the use of the mask with an extended binned fit." ] }, { "cell_type": "code", "execution_count": null, "id": "ruled-society", "metadata": {}, "outputs": [], "source": [ "def integral(xe, s, b, mu, sigma, tau):\n", " return (s * truncnorm.cdf(xe, *xr, mu, sigma) +\n", " b * truncexpon.cdf(xe, *xr, 0, tau))\n", "\n", "c = cost.ExtendedBinnedNLL(n, xe, integral)\n", "\n", "# we set the signal amplitude to zero and fix all signal parameters\n", "m = Minuit(c, s=0, b=1500, mu=1, sigma=0.2, tau=2)\n", "\n", "m.limits[\"s\", \"b\", \"sigma\", \"tau\"] = (0, None)\n", "m.fixed[\"s\", \"mu\", \"sigma\"] = True\n", "\n", "# we temporarily mask out the signal\n", "c.mask = (cx < 0.5) | (1.5 < cx)\n", "\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "9424b64d", "metadata": {}, "source": [ "We plot the intermediate result. Points which have been masked out are shown with open markers." ] }, { "cell_type": "code", "execution_count": null, "id": "happy-diabetes", "metadata": {}, "outputs": [], "source": [ "for ma, co in ((c.mask, \"k\"), (~c.mask, \"w\")):\n", " plt.errorbar(cx[ma], n[ma], n[ma] ** 0.5, fmt=\"o\", color=co, mec=\"k\", ecolor=\"k\")\n", "plt.stairs(np.diff(integral(xe, *[p.value for p in m.init_params])), xe,\n", " ls=\":\", label=\"init\")\n", "plt.stairs(np.diff(integral(xe, *m.values)), xe, label=\"fit\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "heard-jurisdiction", "metadata": {}, "source": [ "Now we fix the background and fit only the signal parameters." ] }, { "cell_type": "code", "execution_count": null, "id": "accredited-dispute", "metadata": {}, "outputs": [], "source": [ "c.mask = None # remove mask\n", "m.fixed = False # release all parameters\n", "m.fixed[\"b\"] = True # fix background amplitude\n", "m.values[\"s\"] = 100 # do not start at the limit\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "timely-afternoon", "metadata": {}, "source": [ "Finally, we release all parameters and fit again to get the correct uncertainty estimates." ] }, { "cell_type": "code", "execution_count": null, "id": "recreational-pride", "metadata": {}, "outputs": [], "source": [ "m.fixed = None\n", "m.migrad()" ] }, { "cell_type": "markdown", "id": "correct-notice", "metadata": {}, "source": [ "We get the same result as before. Since this was an easy problem, we did not need these extra steps, but doing this can be helpful to fit lots of histograms without adjusting each fit manually." ] }, { "cell_type": "markdown", "id": "tough-europe", "metadata": {}, "source": [ "### Weighted histograms\n", "\n", "The cost functions for binned data also support weighted histograms. Just pass an array with the shape `(n, 2)` instead of `(n,)` as the first argument, where the first number of each pair is the sum of weights and the second is the sum of weights squared (an estimate of the variance of that bin value)." ] }, { "cell_type": "markdown", "id": "gothic-regular", "metadata": {}, "source": [ "## Least-squares fits\n", "\n", "A cost function for a general weighted least-squares fit (aka chi-square fit) is also included. In statistics this is called non-linear regression.\n", "\n", "In this case you need to provide a model that predicts the y-values as a function of the x-values and the parameters. The fit needs estimates of the y-errors. If those are wrong, the fit may be biased. If your data has errors on the x-values as well, checkout the tutorial about automatic differentiation, which includes an application of that to such fits." ] }, { "cell_type": "code", "execution_count": null, "id": "packed-penguin", "metadata": {}, "outputs": [], "source": [ "def model(x, a, b):\n", " return a + b * x ** 2\n", "\n", "rng = np.random.default_rng(4)\n", "\n", "truth = 1, 2\n", "x = np.linspace(0, 1, 20)\n", "yt = model(x, *truth)\n", "ye = 0.4 * x**5 + 0.1\n", "y = rng.normal(yt, ye)\n", "\n", "plt.plot(x, yt, ls=\"--\", label=\"truth\")\n", "plt.errorbar(x, y, ye, fmt=\"ok\", label=\"data\")\n", "plt.legend();" ] }, { "cell_type": "code", "execution_count": null, "id": "arabic-plant", "metadata": {}, "outputs": [], "source": [ "c = cost.LeastSquares(x, y, ye, model)\n", "m1 = Minuit(c, a=0, b=0)\n", "m1.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "id": "7ad47416", "metadata": {}, "source": [ "We can also plot the standard visualization manually and add further graphs to the figure." ] }, { "cell_type": "code", "execution_count": null, "id": "former-dominant", "metadata": {}, "outputs": [], "source": [ "m1.visualize()\n", "plt.plot(c.x, model(c.x, *truth), ls=\"--\", label=\"truth\")" ] }, { "cell_type": "markdown", "id": "fa93b807", "metadata": {}, "source": [ "We can also fit a multivariate model, in this case we fit a plane in 2D." ] }, { "cell_type": "code", "execution_count": null, "id": "c253cfa6", "metadata": {}, "outputs": [], "source": [ "def model2(x_y, a, bx, by):\n", " x, y = x_y\n", " return a + bx * x + by * y\n", "\n", "# generate a regular grid in x and y\n", "x = np.linspace(-1, 1, 10)\n", "y = np.linspace(-1, 1, 10)\n", "X, Y = np.meshgrid(x, y)\n", "x = X.flatten()\n", "y = Y.flatten()\n", "\n", "# model truth\n", "Z = model2((x, y), 1, 2, 3)\n", "\n", "# add some noise\n", "rng = np.random.default_rng(1)\n", "Zerr = 1\n", "Z = rng.normal(Z, Zerr)\n", "\n", "plt.scatter(x, y, c=Z)\n", "plt.colorbar();" ] }, { "cell_type": "code", "execution_count": null, "id": "766174be", "metadata": {}, "outputs": [], "source": [ "c2 = cost.LeastSquares((x, y), Z, Zerr, model2)\n", "m2 = Minuit(c2, 0, 0, 0)\n", "m2.migrad()" ] }, { "cell_type": "markdown", "id": "2f3f181e", "metadata": {}, "source": [ "Multivarate fits are difficult to check by eye. Here we use color to indicate the function value.\n", "\n", "To guarantee that plot of the function and the plot of the data use the same color scale, we use the same normalising function for pyplot.pcolormesh and pyplot.scatter." ] }, { "cell_type": "code", "execution_count": null, "id": "bdf44a64", "metadata": {}, "outputs": [], "source": [ "xm = np.linspace(-1, 1, 100)\n", "ym = np.linspace(-1, 1, 100)\n", "Xm, Ym = np.meshgrid(xm, ym)\n", "xm = Xm.flatten()\n", "ym = Ym.flatten()\n", "\n", "qm = plt.pcolormesh(Xm, Ym, model2((xm, ym), *m2.values).reshape(Xm.shape))\n", "plt.scatter(c2.x[0], c2.x[1], c=c2.y, edgecolors=\"w\", norm=qm.norm)\n", "plt.colorbar();" ] }, { "cell_type": "markdown", "id": "complete-howard", "metadata": {}, "source": [ "### Robust least-squares\n", "\n", "The builtin least-squares function also supports robust fitting with an alternative loss functions. See the documentation of `iminuit.cost.LeastSquares` for details. Users can pass their own loss functions. Builtin loss functions are:\n", "\n", "* `linear` (default): gives ordinary weighted least-squares\n", "* `soft_l1`: quadratic ordinary loss for small deviations ($\\ll 1\\sigma$), linear loss for large deviations ($\\gg 1\\sigma$), and smooth interpolation in between\n", "\n", "Let's create one outlier and see what happens with ordinary loss." ] }, { "cell_type": "code", "execution_count": null, "id": "seasonal-singles", "metadata": {}, "outputs": [], "source": [ "c.y[3] = 3 # generate an outlier\n", "\n", "m3 = Minuit(c, a=0, b=0)\n", "m3.migrad()" ] }, { "cell_type": "code", "execution_count": null, "id": "available-organic", "metadata": {}, "outputs": [], "source": [ "m3.visualize()\n", "plt.plot(c.x, model(c.x, 1, 2), ls=\"--\", label=\"truth\")" ] }, { "cell_type": "markdown", "id": "helpful-train", "metadata": {}, "source": [ "The result is distorted by the outlier. Note that the error did not increase! The size of the error computed by Minuit does **not** include mismodelling.\n", "\n", "We can repair this with by switching to \"soft_l1\" loss." ] }, { "cell_type": "code", "execution_count": null, "id": "cheap-truth", "metadata": {}, "outputs": [], "source": [ "c.loss = \"soft_l1\"\n", "m3.migrad()" ] }, { "cell_type": "code", "execution_count": null, "id": "regulated-default", "metadata": {}, "outputs": [], "source": [ "m3.visualize()\n", "plt.plot(c.x, model(c.x, *truth), ls=\"--\", label=\"truth\");" ] }, { "cell_type": "markdown", "id": "attractive-porcelain", "metadata": {}, "source": [ "The result is almost identical as in the previous case without an outlier.\n", "\n", "Robust fitting is very useful if the data are contaminated with small amounts of outliers. It comes with a price, however, the uncertainties are in general larger and the errors computed by Minuit are not correct anymore.\n", "\n", "Calculating the parameter uncertainty properly for this case requires a so-called sandwich estimator, which is currently not implemented. As an alternative, one can use the bootstrap to compute parameter uncertaintes. We use the `resample` library to do this." ] }, { "cell_type": "code", "execution_count": null, "id": "1e9732ca", "metadata": {}, "outputs": [], "source": [ "from resample.bootstrap import variance as bvar\n", "\n", "def fit(x, y, ye):\n", " c = cost.LeastSquares(x, y, ye, model, loss=\"soft_l1\")\n", " m = Minuit(c, a=0, b=0)\n", " m.migrad()\n", " return m.values\n", "\n", "berr = bvar(fit, c.x, c.y, c.yerror, size=1000, random_state=1) ** 0.5\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(10, 4))\n", "for i, axi in enumerate(ax):\n", " axi.errorbar(0, m1.values[i], m1.errors[i], fmt=\"o\")\n", " axi.errorbar(1, m3.values[i], m3.errors[i], fmt=\"o\")\n", " axi.errorbar(2, m3.values[i], berr[i], fmt=\"o\")\n", " axi.set_xticks(np.arange(3), (\"no outlier\", \"Minuit, soft_l1\", \"bootstrap\"));" ] }, { "cell_type": "markdown", "id": "e4dad3f9", "metadata": {}, "source": [ "In this case, Minuit's estimate is similar to the bootstrap estimate, but that is not generally true when the \"soft_l1\" loss is used.\n", "\n", "Robust fits are very powerful when the outliers cannot be removed by other means. If one can identify outliers by other means, it is better to remove them. We manually remove the point (using the mask attribute) and switch back to ordinary loss." ] }, { "cell_type": "code", "execution_count": null, "id": "indoor-wallet", "metadata": {}, "outputs": [], "source": [ "c.mask = np.arange(len(c.x)) != 3\n", "c.loss = \"linear\"\n", "m4 = Minuit(c, a=0, b=0)\n", "m4.migrad()" ] }, { "cell_type": "markdown", "id": "varied-rhythm", "metadata": {}, "source": [ "Now the uncertainties are essentially the same as before adding the outlier." ] }, { "cell_type": "code", "execution_count": null, "id": "abaee0b1", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(10, 4))\n", "for i, axi in enumerate(ax):\n", " axi.errorbar(0, m1.values[i], m1.errors[i], fmt=\"o\")\n", " axi.errorbar(1, m3.values[i], m3.errors[i], fmt=\"o\")\n", " axi.errorbar(2, m3.values[i], berr[i], fmt=\"o\")\n", " axi.errorbar(3, m4.values[i], m4.errors[i], fmt=\"o\")\n", " axi.set_xticks(np.arange(4), (\"no outlier\", \"Minuit, soft_l1\", \"bootstrap\", \"outlier removed\"));" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.9" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 5 } iminuit-2.24.0/doc/notebooks/cython.ipynb0000644000000000000000000001152314332717401015326 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using Cython\n", "\n", "We show how to use Cython to accelerate the computation of a cost function and how to avoid some pitfalls.\n", "\n", "If you do not care specifically about [Cython](https://cython.org) and just want to make your code faster, prefer [Numba](https://numba.pydata.org) (see the corresponding Numba tutorial for more details), or try to run iminuit in the PyPy interpreter. Numba is more powerful and easier to use, and you don't have to learn the awkward Cython dialect. Cython is a good choice when you have to call into C code from Python, but it is not a good choice to call into C++ code, for this [pybind11](https://pybind11.readthedocs.io/en/stable/) is the ideal choice. Cython does not fully support the C++ language, it was designed for C.\n", "\n", "With that disclaimer out of the way, let's see how to use iminuit with a Cython-compiled function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# setup of the notebook\n", "%load_ext Cython\n", "from iminuit import Minuit, describe\n", "import numpy as np\n", "import traceback" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following cell is Cython code and will be compiled to machine code behind the scenes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%cython\n", "\n", "def cython_func(double x, double y, double z):\n", " return (x - 1.) ** 2 + (y - 2.) ** 2 + (z - 3.) ** 2 + 1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unfortunately, if we try to pass starting values to Minuit via keywords, we get a failure." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " m = Minuit(cython_func, x=1, y=2, z=3)\n", "except:\n", " traceback.print_exc()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happened? `Minuit` uses the `describe` tool which uses introspection to read the function signature, but this failed here. Without that, Minuit does not know how many parameters this function accepts and their names.\n", "\n", "Python built-in functions (like `min`) normally do not have a function signature. Functions from cython and swig also do not have one.\n", "\n", "There are a few ways to fix this.\n", "\n", "- One can pass parameter names explicitly to Minuit, then it works.\n", "- One can use positional arguments.\n", "- One can tell Cython to embed a signature." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(cython_func, name=(\"x\", \"y\", \"z\"), x=0, y=0, z=0)\n", "m.errordef = Minuit.LEAST_SQUARES\n", "m.migrad()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, one can use positional arguments without specifying parameter names." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(cython_func, 0, 0, 0)\n", "m.errordef = Minuit.LEAST_SQUARES\n", "m.migrad()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A nicer solution is to ask Cython to add the missing function signature. This can be achieved with the `embedsignature(true)` decorator." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%cython\n", "cimport cython\n", "\n", "@cython.embedsignature(True) # generate a signature that iminuit can extract\n", "def cython_f(double x, double y, double z):\n", " return (x - 1.) ** 2 + (y - 2.) ** 2 + (z - 3.) ** 2 + 1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now it works." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(cython_f, x=0, y=0, z=0)\n", "m.errordef = Minuit.LEAST_SQUARES\n", "m.migrad()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/error_bands.ipynb0000644000000000000000000073042714332717401016335 0ustar00{ "cells": [ { "cell_type": "markdown", "id": "frozen-raising", "metadata": {}, "source": [ "# How to draw error bands\n", "\n", "We show two ways to compute 1-sigma bands around a fitted curve.\n", "\n", "Whether the curve describes a probability density (from a maximum-likelihood fit) or an expectation (from a least-squares fit) does not matter, the procedure is the same. We demonstrate this on an unbinned extended maximum-likelihood fit of a Gaussian." ] }, { "cell_type": "code", "execution_count": 1, "id": "white-dress", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABWOElEQVR4nO3de1xUdfoH8M+ZGYaLchHlaiqolRIq3lCTNlNMyzTTrTTd1NpMwy2jX6ZtaVp5qdZczdWuWqlpteu9SMPU2lQMxY3wLqgpDCo6IMhlZs7vD2LkwADDcGbOYfi8X695wXnO9/s9z5wZZh7OVRBFUQQRERGRimiUToCIiIioKhYoREREpDosUIiIiEh1WKAQERGR6rBAISIiItVhgUJERESqwwKFiIiIVIcFChEREamOTukEHGGxWHDx4kX4+vpCEASl0yEiIiI7iKKIgoIChIeHQ6OpfRtJoyxQLl68iDZt2iidBhERETng/PnzuOWWW2pt0ygLFF9fXwDlT9DPz0/hbIiIiMge+fn5aNOmjfV7vDaNskCp2K3j5+fHAoWIiKiRsefwDB4kS0RERKrDAoWIiIhUhwUKERERqQ4LFCIiIlIdFihERESkOixQiIiISHVYoBAREZHqsEAhIiIi1WGBQkRERKrDAoWIiIhUhwUKERERqQ4LFCIiIlIdFihERNRge/fuxfDhwxEeHg5BELBp0yab7ZYvX46IiAh4eXmhT58+SElJcaiNq8Z11KRJk/DKK6/INp4969fR56e2dVeBBQoRNUpFpSZEzNyOiJnbUVRqUjqdJq+wsBDdunXD8uXLa2yzYcMGJCYmYs6cOTh06BC6deuGIUOGIDc3t15tXDVuZQMGDMDq1avtams2m7Ft2zaMGDHCrvb2qGv9Ovr8XLHuHCY2QkajUQQgGo1GpVMhIoUUlpSJ7V7aJrZ7aZtYWFKmdDqqdurUKRGAuHXrVnHgwIGit7e3eNttt4n79+93yvIAiBs3bqwWj42NFRMSEqzTZrNZDA8PFxcsWFCvNq4at7K7775bXLVqlV1t9+7dK4aFhYkWi8Wu9vVla/06+vxcse4qq8/3N7egEBG5uSNHjkAQBCxevBivvvoqjhw5grZt22LmzJnV2s6fPx/Nmzev9XHu3Ll651BaWorU1FTEx8dbYxqNBvHx8di3b5/dbVw1bkNs2bIFw4cPhyAIso9ti6PPT43rrjKdU0cnIiLFHTlyBAEBAdiwYQOCgoIAACNGjMD7779fre2UKVPwyCOP1DpeeHh4vXO4fPkyzGYzQkJCJPGQkBAcO3bM7jauGrchNm/ejHfffVf2cWvi6PNT47qrjAUKEZGbO3LkCB588EFrcQIAmZmZ6NixY7W2gYGBCAwMdGV6qjR//nzMnz/fOn3jxg3s378f06ZNs8YyMjLQtm1bSb+jR4/i4sWLGDRoULUxZ86ciUWLFtW63KNHj6JTp04NzN49sEAhInJzR44cwaxZsySxtLQ0/OlPf6rWtuoXsy22vpjr0qpVK2i1WhgMBkncYDAgNDTU7jauGrfqlqRx48Zh9OjRGDVqlDVma0vSli1bMHjwYHh5eVWb98ILL2DixIk2l1ehffv2tc63xZHnZ28/R8eWA49BISJyY0ajEVlZWejevbsknpaWhpiYmGrtp0yZgrS0tFofjuzi0ev16NmzJ5KTk60xi8WC5ORk9OvXz+42rho3MDAQHTt2tD68vb0RHBwsiel01f/H37x5Mx588EGbYwYFBaFTp061PvR6fU2rsEaOPD97+zk6tizqfQiuCvAsHiLiWTz22bt3r6jT6cQbN25YY1lZWSIAMTMzU7blFBQUiIcPHxYPHz4sAhAXL14sHj58WDx79qy1zfr160VPT09x9erVYkZGhjh58mQxICBAzMnJqVebZcuWiQMHDpR93NrYcxaPwWAQPTw8xEuXLtk1Zn3UtX4dWW/29mvouqusPt/fLFCIqFFigWKfZcuWiXfccYcktnHjRjEgIEDW5fzwww8igGqPCRMmVMunbdu2ol6vF2NjY22e6lxXmzlz5ojt2rWTfdza2FOgfPTRR2L//v3tHrM+7Fm/jqw3e/rZ28Ye9fn+FkRRFJ27jUZ++fn58Pf3h9FohJ+fn9LpEJECikpNiJr9HQAgY94Q+Oh5SB0pa8SIEYiLi8OMGTOUTkW16vP9zWNQiIiIZBAXF4exY8cqnYbb4L8cREREMuCWE3lxCwoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBGRqixfvhwRERHw8vJCnz59kJKS0uA+EREREASh2iMhIcEpz2HSpEl45ZVXZBvPGeukoKAA06dPR7t27eDt7Y0777wTBw8elC3nhmKBQkREqrFhwwYkJiZizpw5OHToELp164YhQ4YgNze3QX0OHjyI7Oxs62Pnzp0AgIcffrjOnAYMGIDVq1fb/RzMZjO2bduGESNG2N2nNs5aJ3/961+xc+dOfP755/j1119x7733Ij4+HhcuXJAl7wZz6FJwCuOVZImIV5K1348//ljtcveZmZkiADErK0vBzKqLjY0VExISrNNms1kMDw8XFyxYIGuf5557TuzQoYNosVjqzMmeq8hWtnfvXjEsLMyuse3hjHVSVFQkarVacdu2bZJ+PXr0EP/+97/Lkrct9fn+5hYUIiI3l5aWhs6dO0vusHv48GG0aNEC7dq1k7SdP38+mjdvXuvj3LlzTsmztLQUqampiI+Pt8Y0Gg3i4+Oxb98+WfusWbMGTzzxBARBkPdJoPyOxsOHD5dlbGetE5PJBLPZXO2uy97e3vjpp58anLcceKE2IiI3d+TIEZt3M+7WrVu1tlOmTMEjjzxS63iO3M3YHpcvX4bZbEZISIgkHhISgmPHjsnWZ9OmTbh27RomTpwoS95Vbd68Ge+++64sYzlrnfj6+qJfv354/fXX0blzZ4SEhOCLL77Avn370LFjR1lybyhuQSEicnNpaWmIiYmRxA4fPlwtBgCBgYHo2LFjrQ+drvb/bWfOnGnzgNTKj5q+XF3h448/xn333VdjoVV1K9KPP/6IKVOm2LUV6ejRo7h48SIGDRpUbZ7a1svnn38OURTRunVreHp6YunSpRg7diw0GnWUBvXOYu/evRg+fDjCw8MhCAI2bdokmS+KImbPno2wsDB4e3sjPj4eJ0+elLTJy8vDuHHj4Ofnh4CAADz55JO4fv16g54IERFVZzabkZ6eXm0LyqFDh2wWKHLs4nnhhRdw9OjRWh/t27ev1q9Vq1bQarUwGAySuMFgQGhoqM1l1bfP2bNn8f333+Ovf/1rjflPmTIFaWlp1kevXr0wb948Saym4mbLli0YPHhwtV0njq4XZ66TDh06YM+ePbh+/TrOnz+PlJQUlJWV2XxtlFDvXTyFhYXo1q0bnnjiCYwaNara/LfeegtLly7Fp59+isjISLz66qsYMmQIMjIyrC/YuHHjrEdRl5WVYdKkSZg8eTLWrVvX8GdERERWx48fR3FxseQLdd++fbhw4YLNAkWOXTxBQUEICgqqd656vR49e/ZEcnIyRo4cCQCwWCxITk7GtGnTZOmzatUqBAcHY9iwYTXmERgYiMDAQOu0t7c3goOD7dr1sXnzZkyePNnmPEfWiyvWSbNmzdCsWTNcvXoV3333Hd5666165eg0DTkaF4C4ceNG67TFYhFDQ0PFt99+2xq7du2a6OnpKX7xxReiKIpiRkaGCEA8ePCgtc23334rCoIgXrhwwa7l8iweIuJZPPZZu3atCECcNm2aeOLECfGbb74RO3bsKAIQU1JSlE6vmvXr14uenp7i6tWrxYyMDHHy5MliQECAmJOTI4qiKC5btkwcOHBgvfpUMJvNYtu2bcWXXnqpXjnZexaPwWAQPTw8xEuXLtVr/LrY8/yqrhd7+iQlJYnffvuteObMGXHHjh1it27dxD59+oilpaWy5l9Zfb6/ZT1INjMzEzk5OZIjh/39/dGnTx/s27cPY8aMwb59+xAQEIBevXpZ28THx0Oj0eDAgQN46KGHqo1bUlKCkpIS63R+fr6caRMRua20tDQMGTIEZ86cQZcuXRAVFYW5c+di6tSpWLp0KT7//HOlU5R49NFHcenSJcyePRs5OTmIiYlBUlKS9YDPy5cv4/Tp0/XqU+H777/HuXPn8MQTTzgl961btyI2NhatWrWSdVx7nl/V9WJPH6PRiFmzZuH3339HYGAgRo8ejTfffBMeHh6y5u8oQRRF0eHOgoCNGzdaNyH9/PPP6N+/Py5evIiwsDBru0ceeQSCIGDDhg2YP38+Pv30Uxw/flwyVnBwsPWPpqrXXnsNc+fOrRY3Go3w8/NzNH0iasSKSk2Imv0dACBj3hD46HlSoi1DhgxB79698cYbbyiditsbMWIE4uLiMGPGDKVTUa38/Hz4+/vb9f2tjkN16zBr1iwYjUbr4/z580qnRETUKBw5cgRdunRROo0mIS4uDmPHjlU6Dbch678cFUcHGwwGyRYUg8FgPRgrNDS02uV5TSYT8vLyajwi2dPTE56ennKmSkTk9nJycmAwGFiguAi3nMhL1i0okZGRCA0NRXJysjWWn5+PAwcOoF+/fgCAfv364dq1a0hNTbW22bVrFywWC/r06SNnOkRETVpoaChEUURUVJTSqRDVW723oFy/fh2nTp2yTmdmZiItLQ2BgYFo27Ytpk+fjjfeeAO33nqr9TTj8PBw63EqnTt3xtChQ/HUU09h5cqVKCsrw7Rp0zBmzBinXZ2QiIiIGpd6Fyi//PIL7rnnHut0YmIiAGDChAlYvXo1ZsyYgcLCQkyePBnXrl1DXFwckpKSJBetWbt2LaZNm4ZBgwZBo9Fg9OjRWLp0qQxPh4iIiNxBg87iUUp9jgImIvfEs3iIGh+3O4uHiIiImhYWKERERKQ6LFCIiEgWy5cvR0REBLy8vNCnTx+kpKTU2t5sNuPVV19FZGQkvL290aFDB7z++uuofOTBa6+9Vu2Ov506dXLac5g0aRJeeeUVWces73qxt8+FCxcwfvx4tGzZEt7e3ujSpQt++eUXWXNXEgsUIiJqsA0bNiAxMRFz5szBoUOH0K1bNwwZMqTada8qW7RoEVasWIH33nsPR48exaJFi/DWW29h2bJlknZ33HEHsrOzrY+ffvrJ7rwGDBiA1atX29XWbDZj27ZtGDFihN3j18WR9WJPn6tXr6J///7w8PDAt99+i4yMDPzjH/9AixYtZMtdcU67I5AT8WaBRO7NnhsB8maB9bNv3z5x4MCBYmBgoAhA8pDjszQ2NlZMSEiwTpvNZjE8PFxcsGBBjX2GDRsmPvHEE5LYqFGjxHHjxlmn58yZI3br1s3hvOy90Z8oiuLevXvFsLAw0WKxOLy8qhxZL/b0eemll8S4uDjZ8nSV+nx/cwsKEZGbO3LkCAYMGIDu3bvjxx9/RFJSEgIDAzFo0CBs2LBBcjbF/Pnz0bx581of586dk4xfWlqK1NRUyY1iNRoN4uPjsW/fvhrzuvPOO5GcnIwTJ05Y8/zpp59w3333SdqdPHkS4eHhaN++PcaNG1dt+XLZsmULhg8fDkEQZBnPkfVib58tW7agV69eePjhhxEcHIzu3bvjww8/lCVvteB5eUREbu7ZZ5/FqFGj8M477wAAoqKiMHbsWKSmpuKRRx6RtJ0yZUq1WFVVL6p5+fJlmM3mancPDgkJwbFjx2ocZ+bMmcjPz0enTp2g1WphNpvx5ptvYty4cdY2ffr0werVq3H77bcjOzsbc+fOxV133YX09HT4+vra9fzttXnzZrz77ruyjefIerG3z5kzZ7BixQokJibi5ZdfxsGDB/Hss89Cr9djwoQJsj0HJbFAISJyYwaDAT/99BP27NkjiTdr1szmloLAwEAEBga6JLcvv/wSa9euxbp163DHHXcgLS0N06dPR3h4uPVLtvLWlK5du6JPnz5o164dvvzySzz55JPVxpw/fz7mz59vnb5x4wb279+PadOmWWMZGRlo27atpN/Ro0dx8eJFDBo0qNqYM2fOxKJFi2p9LkePHnXqwbtVWSwW9OrVy/pcu3fvjvT0dKxcuZIFChERqV9qaiosFgu6detWLd6rV69q7at+wdtS9Qu+VatW0Gq1MBgMknYGg6HGm8ACwIsvvoiZM2dizJgxAIAuXbrg7NmzWLBgQY1fsgEBAbjtttskt1yprOoWoHHjxmH06NEYNWqUNWbrtipbtmzB4MGDJVc9r/DCCy9g4sSJNT4PAGjfvn21mCPrxd4+YWFh1e6x1LlzZ/z73/+uNc/GhAUKEZEbs1gsAIDCwkLrLpH//e9/2Lt3L954441q7R3ZxaPX69GzZ08kJydb77tmsViQnJws2XJRVVFRETQa6aGQWq3WmrMt169fx+nTp/GXv/zF5vyqW4C8vb0RHByMjh071vqcNm/ejMmTJ9ucFxQUhKCgoFr72+LIerG3T//+/XH8+HFJ3xMnTqBdu3b1zlO1XHDQrux4Fg+Re+NZPPLJzc0Vvb29xfHjx4tHjx4Vt23bJrZr106cPn26rMtZv3696OnpKa5evVrMyMgQJ0+eLAYEBIg5OTnWNsuWLRMHDhxonZ4wYYLYunVrcdu2bWJmZqb4n//8R2zVqpU4Y8YMa5sXXnhB3L17t5iZmSn+97//FePj48VWrVqJubm5duVlz1k8BoNB9PDwEC9dulS/J22HutZL1XViTx9RFMWUlBRRp9OJb775pnjy5Elx7dq1oo+Pj7hmzRrZn4Oc6vP9zS0oRERuLCgoCF9++SVeeOEFdO3aFW3btsW0adOsN3qVy6OPPopLly5h9uzZyMnJQUxMDJKSkiQHe16+fBmnT5+2Ti9btgyvvvoqnnnmGeTm5iI8PBxPP/00Zs+ebW3z+++/Y+zYsbhy5QqCgoIQFxeH/fv3O7RFoyZbt25FbGwsWrVqJduYFepaL1XXiT19AKB3797YuHEjZs2ahXnz5iEyMhJLliyRHGDc2PFmgUSkOvbcCJA3CyS5jBgxAnFxcZgxY4bSqbg93iyQiIjITnFxcRg7dqzSaVAV/JeDiIiaNG45USduQSEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBER1UIURUyePBmBgYEQBAEBAQGYPn260mm5PV6ojYiIqBZJSUlYvXo1du/ejfbt20Oj0cDb29s6PyIiAtOnT2fRIjMWKERERLU4ffo0wsLCcOeddyqdSpPCAoWIiFzLYgFu5Cmbg3cgoKn7KIeJEyfi008/BQAIgoB27dohIiICMTExWLJkCQYMGICzZ8/i+eefx/PPPw+gfJcQNRwLFCIicq0becDbHZTN4cXTQLNWdTb75z//iQ4dOuCDDz7AwYMHodVq8fDDD1vn/+c//0G3bt0wefJkPPXUU87MuMlhgUJERFQDf39/+Pr6QqvVIjQ0tNr8wMBAaLVa+Pr62pxPjuNZPERERKQ6LFCIiIhIdbiLh4iIXMs7sPwYEKVzkIler4fZbJZtPCrHAoWIiFxLo7HrANXGIiIiAnv37sWYMWPg6emJVq3c57kpibt4iIiIGmDevHnIyspChw4dEBQUpHQ6boNbUIiIiGpR9Sqxu3fvlszv27cvjhw54tqkmgBuQSEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBHZoajUhIiZ2xExczuKSk1Kp0Pk9ligEBFRo8FCselggUJERFRPAwYMkFxdluTHAoWIiBoNs0W0/p6SmSeZVqvdu3dDEARcu3ZN6VQaFRYoRETUKCSlZyN+8R7r9MRVBxG3aBeS0rMVzIqchQUKERGpXlJ6NqauOQRDfokknmMsxtQ1h5xapBQWFuLxxx9H8+bNERYWhn/84x+S+Z9//jl69eoFX19fhIaG4rHHHkNubi4AICsrC/fccw8AoEWLFhAEARMnTix/TklJiIuLQ0BAAFq2bIkHHngAp0+fdtrzaGxYoBARkaqZLSLmbs2ArZ05FbG5WzOctrvnxRdfxJ49e7B582bs2LEDu3fvxqFDh6zzy8rK8Prrr+PIkSPYtGkTsrKyrEVImzZt8O9//xsAcPz4cWRnZ+Of//wngPLCJzExEb/88guSk5Oh0Wjw0EMPwWKxOOV5NDY6pRMgIiKqTUpmHrKNxTXOFwFkG4uRkpmHfh1ayrrs69ev4+OPP8aaNWswaNAgAMCnn36KW265xdrmiSeesP7evn17LF26FL1798b169fRvHlzBAYGAgCCg4MREBBgbTt69GjJsj755BMEBQUhIyMD0dHRsj6PxohbUIiISNVyC2ouThxpVx+nT59GaWkp+vTpY40FBgbi9ttvt06npqZi+PDhaNu2LXx9fXH33XcDAM6dO1fr2CdPnsTYsWPRvn17+Pn5ISIiwq5+TQULFCJqlBrj2RzkmGBfL1nbyamwsBBDhgyBn58f1q5di4MHD2Ljxo0AgNLS0lr7Dh8+HHl5efjwww9x4MABHDhwwK5+TYXsBYrZbMarr76KyMhIeHt7o0OHDnj99dchijc/PERRxOzZsxEWFgZvb2/Ex8fj5MmTcqdCRG6KZ3M0LbGRgQjz94JQw3wBQJi/F2IjA2VfdocOHeDh4WEtHgDg6tWrOHHiBADg2LFjuHLlChYuXIi77roLnTp1sh4gW0Gv1wMo/36scOXKFRw/fhyvvPIKBg0ahM6dO+Pq1auy59+YyV6gLFq0CCtWrMB7772Ho0ePYtGiRXjrrbewbNkya5u33noLS5cuxcqVK3HgwAE0a9YMQ4YMQXGx/JvniMi9KHk2BylDqxEwZ3gUAFQrUiqm5wyPglZTUwnjuObNm+PJJ5/Eiy++iF27diE9PR0TJ06ERlP+9dm2bVvo9XosW7YMZ86cwZYtW/D6669LxmjXrh0EQcC2bdtw6dIlXL9+HS1atEDLli3xwQcf4NSpU9i1axcSExNlz78xk71A+fnnn/Hggw9i2LBhiIiIwJ///Gfce++9SElJAVC+9WTJkiV45ZVX8OCDD6Jr16747LPPcPHiRWzatEnudIjIjSh9NgcpZ2h0GFaM74FgP09JPNTfCyvG98DQ6DCnLfvtt9/GXXfdheHDhyM+Ph5xcXHo2bMnACAoKAirV6/GV199haioKCxcuBDvvPOOpH/r1q0xd+5czJw5EyEhIZg2bRo0Gg3Wr1+P1NRUREdH4/nnn8fbb7/ttOfQGAli5X0vMpg/fz4++OAD7NixA7fddhuOHDmCe++9F4sXL8a4ceNw5swZdOjQAYcPH0ZMTIy13913342YmBjr6VeVlZSUoKTk5n9L+fn5aNOmDYxGI/z8/ORMn4hUoKjUhKjZ3wEAMuYNgY++/ITDfaevYOyH++vs/8VTfWU/m6OmnMi1CorL0OW1HQCA1ZN6465bg5yy5YScIz8/H/7+/nZ9f8v+FzZz5kzk5+ejU6dO0Gq1MJvNePPNNzFu3DgAQE5ODgAgJCRE0i8kJMQ6r6oFCxZg7ty5cqdKRI2MkmdzkDpULkZiIwNZnLgx2QuUL7/8EmvXrsW6detwxx13IC0tDdOnT0d4eDgmTJjg0JizZs2S7Jur2IJCRE2Lms/mINfw0euQtXCY0mmQC8heoLz44ouYOXMmxowZAwDo0qULzp49iwULFmDChAkIDQ0FABgMBoSF3dxnaDAYJLt8KvP09ISnp6fNeUTUdFSczZFjLLZ5HIqA8mMSnHE2BxG5luwHyRYVFVmPbq6g1Wqtl+6NjIxEaGgokpOTrfPz8/Nx4MAB9OvXT+50iMiNKHk2BxG5luwFyvDhw/Hmm29i+/btyMrKwsaNG7F48WI89NBDAABBEDB9+nS88cYb2LJlC3799Vc8/vjjCA8Px8iRI+VOh4jcjJJncxCR68i+i2fZsmV49dVX8cwzzyA3Nxfh4eF4+umnMXv2bGubGTNmoLCwEJMnT8a1a9cQFxeHpKQkeHlxvzER1W1odBj6d2zFszmI3JjsBYqvry+WLFmCJUuW1NhGEATMmzcP8+bNk3vxRNRE8GwOIvfGe/EQERGR6rBAISIiItVhgUJERESqwwKFiIiIVIcFChEREakOCxQiIiJSHRYoREREpDosUIiIiEh1WKAQERGR6rBAISIiItVhgUJERESqwwKFiIiIVIcFChEREakOCxQiIiJSHRYoREREpDosUIiIiEh1WKAQERGR6rBAISKnKio1IWLmdkTM3I6iUpPDbeRaFhE1DixQiIiISHVYoBCR6pgtovX3lMw8yTQRNQ0sUIhIVXZmGBC/eI91euKqg4hbtAtJ6dkKZkVErsYChYhUZfr6NBjySySxHGMxpq45xCKFqAlhgUJEqmJrZ05FbO7WDO7uIWoiWKAQUaMgAsg2FiMlM0/pVIjIBVigEFGjkltQrHQKROQCLFCIqFEJ9vVSOgUicgEWKESkKkIt8TB/L8RGBroyHSJSCAsUIlKdqkVKxfSc4VHQamoqYYjInbBAISJVWTImBsF+npJYqL8XVozvgaHRYQplRUSuplM6ASKiygZHhWBgp2B0eW0HAGD1pN6469YgbjkhamK4BYWIVKdyMRIbGcjihKgJYoFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBERkeqwQCEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBERkeqwQCEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiI7mC2i9feUzDzJNBHJzykFyoULFzB+/Hi0bNkS3t7e6NKlC3755RfrfFEUMXv2bISFhcHb2xvx8fE4efKkM1IhImqwpPRsxC/eY52euOog4hbtQlJ6toJZEbk32QuUq1evon///vDw8MC3336LjIwM/OMf/0CLFi2sbd566y0sXboUK1euxIEDB9CsWTMMGTIExcXFcqdDRNQgSenZmLrmEAz5JZJ4jrEYU9ccYpFC5CQ6uQdctGgR2rRpg1WrVlljkZGR1t9FUcSSJUvwyiuv4MEHHwQAfPbZZwgJCcGmTZswZswYuVMiInKI2SJi7tYM2NqZIwIQAMzdmoHBUaHQagQXZ0fk3mTfgrJlyxb06tULDz/8MIKDg9G9e3d8+OGH1vmZmZnIyclBfHy8Nebv748+ffpg3759NscsKSlBfn6+5EFE5GwpmXnINta8ZVcEkG0sRkpmnuuSImoiZC9Qzpw5gxUrVuDWW2/Fd999h6lTp+LZZ5/Fp59+CgDIyckBAISEhEj6hYSEWOdVtWDBAvj7+1sfbdq0kTttIqpDUakJETO3I2LmdhSVmmqMOTKOM/s5Mk5Fm7Ef7rdrzNwC7p4mkpvsBYrFYkGPHj0wf/58dO/eHZMnT8ZTTz2FlStXOjzmrFmzYDQarY/z58/LmDERUcME+3opnQKR25G9QAkLC0NUVJQk1rlzZ5w7dw4AEBoaCgAwGAySNgaDwTqvKk9PT/j5+UkeRESuEOLniZqOLhEAhPl7ITYy0JUpETUJshco/fv3x/HjxyWxEydOoF27dgDKD5gNDQ1FcnKydX5+fj4OHDiAfv36yZ0OEVGDvHx/ZwCoVqRUTM8ZHsUDZImcQPYC5fnnn8f+/fsxf/58nDp1CuvWrcMHH3yAhIQEAIAgCJg+fTreeOMNbNmyBb/++isef/xxhIeHY+TIkXKnQ0TUIIOjQrBifA8E+3lK4qH+XlgxvgeGRocplBmRe5P9NOPevXtj48aNmDVrFubNm4fIyEgsWbIE48aNs7aZMWMGCgsLMXnyZFy7dg1xcXFISkqClxf34xKR+gyNDkP/jq3Q5bUdAIDVk3rjrluDuOWEyIlkL1AA4IEHHsADDzxQ43xBEDBv3jzMmzfPGYsnIpJd5WIkNjKQxQmRk/FePERERKQ6LFCIiIhIdVigEBERkeqwQCEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBERkeqwQCEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBERkeqwQCEipzJbROvvKZl5kun6tKnKR69D1sJhyFo4DD56ncPjEJE66ZROgIjcV1J6NuZs+c06PXHVQYT4eda7jaPLCvP3wpzhURgaHeZA9kSkJG5BISKnSErPxtQ1h2DIL5HEcytN78ww1NmmIcvKMRZj6ppDSErPrmf2RKQ0FihEJDuzRcTcrRmwtYOlcmz+N0frbFPXbhp7ljV3awZ39xA1MixQiEh2KZl5yDYW19mu6hYPW1LPXm3QskQA2cZipGTm1bksIlIPFihEJLvcgrqLE3tdKqi9iLF3WXLmRETOxwKFiGQX7Osl21hBvrUfMGvvsuTMiYicjwUKEckuNjIQYf5eEOpoF+LnWWebnu1aNGhZAoAwfy/ERgbWsSQiUhMWKEQkO61GwJzhUQBQrXCoPP3y/Z3rbKPV1F7C2LOsOcOj6hyHiNSFBQoROcXQ6DCsGN8DwVWuaRLid3NXy+CokDrbNGRZof5eWDG+B6+DQtQIsUAhIqcZGh2G7xPvtk6vntQbOxP/VO82ji7rp5cGsjghaqR4JVkicqrKu1ZqOg7EnjaOLIu7dYgaL25BISIiItXhFhQicrlo4Qx6aU7AI/ln4PoFeF47jx/1Z3EJ/tD99zeYOw1XOkUiUhgLFCInKyo1IWr2dwCAjHlDrHfeVfvYzshHyP0N+l1zsc0zuTxwoPyHFkAbDdAGl4A9bwJ73sQOfWt8a4mFYLwDCIqQLSdH2xCRa7FAISKnC0EeXtB9Ba+P9kKwedec6m7TXMBtmo0QP/gOuP8d4I5HnZwlEakJCxQich5RhG7fUuz2XABvodShIYSyImDzM9Cf2Qtv3Isb4BVhiZoCFihE5ByiBfj2ZehT3q9+BTUAl0R/tLhjEHQt2qC0WRgSt11ArOYYxgf8D5rrhmrtdb+uxxb9j0goe9YFyROR0ngWDxHJTgML9N8mAinvV5snejTDu2WjcXfJuygd+SEweB5MvZ7CNks/zDZNQvHf0lH8+Lf42lz9Wii3ai5gs/5VaI9tccXTICIFsUAhIllpYcZij39Bl/a5JG4RBZR1n4gbUw/in+bRKKppV42ggeWWWPxf2RRMK/0bRH1zyWxvoRT6zU8DWT856ykQkQqwQCEi+ZhL8Z7HUozU/iwJm0QNni2bhrL7/gE0D7F7uG2Wfih+4gcgtKskLphLgfWPAbnHZEmbiNSHBQoRyUMUod/2N9ynPSgNa/WYWjYd2yz9HBs2sD3w5E6Yuj4mnVFsBNY+DBTkOJoxEakYCxQikoU27TPofvtaGtR5oeThtdhp6dWwwT28UHr/EnxnrjKO8Ryw7hGg5HrDxici1WGBQkQNFiVkQb9jliQm6psB4/8NS/uB8ixEo8VzZQk4bOkojWcfAb6eBJhN8iyHiFSBBQoRNUhzFOE9j6UQzCWSeOnwFUBEnKzLKoYnniz9P1haREpnnNwB/PgPWZdFRMpigUJEjhNFLPD4CO010uNAPjbdB/Ptwxwe1kevQ9bCYchaOKza5fLz4IeSR9YD3lXuerz3LQiGXx1eJhGpCwsUInKY7vCnGK7dL4mlWdpjoWmsU5crtuwIjP0CELQ3gxYTPLdOgwe4q4fIHbBAISLH5B6Dx86XJSHRyx/Typ5DmSsuUt22LxD3vCSkyU3HNN0m5y+biJyOBQoR1Z8oAtsTqx938sB7+F0Mcl0ed88AgqMkoQTtJtwhZLouByJyChYoRFRv2t++As7+VxIr6z0F5tvud20iOk9g5ArJrh6dYME7HisBU0ktHYlI7VigEFG9+KEQ+uQ5kth5SxDKBvxdmYTCY4A//Z8k1FlzHh4/va1MPkQkCxYoRGQXs0UEADyv+xpCYa5k3mumx5Hye7G1jT3jAEBKZp5dfersd9f/ASFdJO11+5cBl085tHxHcyQi+Ti9QFm4cCEEQcD06dOtseLiYiQkJKBly5Zo3rw5Ro8eDYOh+u3ViUgdktKzEb94D6KELDyu3SGZ9725O5ItPTFx1UHEL95T6zg7MwySNhNXHUTcol3YmVH733/F8qv2S0rPLg/o9MDIf0HU3Dw4V7CYgJ2z6718R3MkInk5tUA5ePAg3n//fXTtKr3R1/PPP4+tW7fiq6++wp49e3Dx4kWMGjXKmakQkYOS0rMxdc0h5ObfwOseq6AVbm5NKBY98JppgnU6N7/24z6mr0+DoUqbHGMxpq9Pq3P5tvpNXXPoZpES1hWm2KnSzse3Q3P25l2P7Vm+IzkSkfycVqBcv34d48aNw4cffogWLVpY40ajER9//DEWL16MgQMHomfPnli1ahV+/vln7N+/v5YRicjVzBYRc7dmQATwZ+1e9NSclMxfbnoQv4vB1mmxSt+qbO0oqW3nSeXl19Rv7tYM67LK7kzEFdFX0k6fPBsCLHYvv745EpFzOK1ASUhIwLBhwxAfHy+Jp6amoqysTBLv1KkT2rZti3379tkcq6SkBPn5+ZIHETlfSmYeso3F8EExZujWS+ZlWkLwgfmBGvumnr1q93JqKgAqll9bv2xjMVIy88oDXn541/RnSRtNzhE8pPmpemc7ll/fNkQkH6cUKOvXr8ehQ4ewYMGCavNycnKg1+sREBAgiYeEhCAnx/Zt0xcsWAB/f3/ro02bNs5Im4iqyC0oLw4maZMQJEj/MXjNNBEl0NfY91JBw0/zrVh+fdp9YR6Ik5bWkvkvenwJL/C0Y6LGRPYC5fz583juueewdu1aeHl5yTLmrFmzYDQarY/z58/LMi4R1S7Y1wv+uI6nddsk8WRzd+yxdKu1b5CvpyzLr287M7R40/SYZH6YkIentNsbnA8RuY7sBUpqaipyc3PRo0cP6HQ66HQ67NmzB0uXLoVOp0NISAhKS0tx7do1ST+DwYDQ0FCbY3p6esLPz0/yICLni40MxAvNkuAnFEni75geqbNvz3YtqsWEGtrWFI+NDESYv1et/cL8vRAbKb1x4G5LDMwRd0tiU3RbEQzbu52EGn63J0cicg7ZC5RBgwbh119/RVpamvXRq1cvjBs3zvq7h4cHkpOTrX2OHz+Oc+fOoV+/fnKnQ0QNoC3MxTh8I4ltMffDUbGdzfaVv8S1Gttf6VWjtX3xazUC5gyPqrXfnOFRNpYloDT+dUmvZkIJntX9x67l1ydHInIO2QsUX19fREdHSx7NmjVDy5YtER0dDX9/fzz55JNITEzEDz/8gNTUVEyaNAn9+vVD37595U6HiBrix3egNd88vsMkaqwHoQb4eCDA20PSPMSv9l0yS8bEINhPuusn1N8LS8bE1NhnaHQYVozvYbPfivE9MDQ6zGY/MfgOoPt4Sewxjz3o4is9lqbq8h3JkYjk54Jbjlb37rvvQqPRYPTo0SgpKcGQIUPwr3/9S4lUiKgmV88Cv6yShL4y341MMQyrJ/XGXbcGoajUhC6vlV+4bfWk3ujZroV12pbBUSEY2ClY0ueuW4NQYjLXmsrQ6DD079iqWr+attJY3fN3iP/70npTQ41owtfRB3DbvsE1Lt/RHIlIXi4pUHbv3i2Z9vLywvLly7F8+XJXLJ6IHLFnEWAps06KWj2WFpdfUDE2MhBajSApEKoeB1KTqn3qLDIa0s8vDKaYv8Aj9SNryON/axGGHshGyxrHcTRHIpIP78VDRNVdOg4c+UISMvV4AtloqVBCjjPd+RxKxEqXwDeXYqpui4IZEZE9WKAQUXV73wFEy81pj2You3O6Yuk0hOgbjvXmeySxR7U/IBRXFMqIiOzBAoWIpPIygfSvpbG+U4FmQcrkI4MVphGSrSiegolbUYhUjgUKEUn995/Vtp6gX4Jy+cggBy2xocpWlDHaHyAUXFQoIyKqCwsUIropPxtIWyuN9X4C8LHvAFg1W2EaAVF789L8noIJup//qWBGRFQbFihEdNO+9wBz6c1prR7o27i3nlTIRkuYuo2TxHRpnwMFtu8BRkTKYoFCROWK8qpd9wQx4wA/2xdCa4xMdz4PUXPz4nKCuQTYv0LBjIioJixQiKjcgfeBssKb04IG6P+ccvk4gejXGuauY6XBXz4BSvJtd6jER69D1sJhyFo4DD56Ra5xSdSksEAhIqCkADiwUhqL/jMQGKlMPk5U1icBFrHShddK8qE7/JlyCRGRTSxQiAhIXQ0UX5PG4p5XIhOnE1t2xHeWXpKYLmUFPGBSKCMisoUFClFTZyoBfn5PGrt9GBASpUw+LvC+abhkWnM9ByO1PymUDRHZwgKFqKn79SvgepUzWe5KVCYXF0kTO+KApZMkNlm7HQIsNfQgIldjgUKkAkWlJkTM3I6ImdtRVGp7V4M9bezpJ5kuKau29cTcNg5FwTEOLasxWVFlK8qtmgsYqDlsd39br4czX0eipoYFClETpjmzC7h0VBIr6ztNoWxca7clBpagzpLY07ptCmVDRFWxQCFqwjwOSLeenLS0hqXDIIWycTWhWjEWqzkOze8pCuVDRJWxQCFqoqKELGiz9kpiH5nvL7/+SRNhjhoF+LWWxHT7lyqUDRFV1nQ+iYhI4kndN5LpS6IfNpn7K5SNQrT6ajdC1J5IgpB3RqGEiKgCCxSiJigUVzBCs08S+8x0L0qgr6GHG+vxOERPX+ukABG6Xz5QMCEiAligEDVJE3U74CGYrdOizhtrzPEKZqQgT1+YYh6XhHRH1sEP1xVKiIgAFihETme2iNbfUzLzJNOKjF1SgMe0yZKQqesYXIWfMvk4mT05lfT4K0zizY9DoawQY7U/uCQ/IrKNBQqREyWlZyN+8R7r9MRVBxG3aBeS0rMVG1t3ZC38hKJKEQGm2KkNzmdnhsFmPjszDA0e21G21lHl6Yo2gz4+g28tsZL4RN130PHy90SKYYFC5CRJ6dmYuuYQDPklkniOsRhT1xxqUJGyM8Pg2NgWc/XjK26/H2JgB4dzqTB9fZrNfKavT2vw2I6oaR3lVpqu3OYj0/2SdmFCHu7X8JRjIqWwQCFyArNFxNytGbC1g6MiNndrhsO7QOZ/c9ShsbUnvoHm2llp8E55LsxWWz5KqGsdVW1zROyIXyy3Sdo+qfsGZjMvf0+kBBYoRE6QkpmHbGNxjfNFANnGYqRk5jk0ftWtAvaOrUtZKZk2h3YD2vZzKAd7KVWk1LaOampTdStKN80ZnPplp6x5EZF9WKAQOUFuQc3FiSPt5Mihi3AG2t/3S2Km2KmAIDgth8Zmh6UXzlmCJLHg9I8UyoaoaWOBQuQEwb5esraTI4eqF2bLEVvA3PlBpy2/MbJAg1XmoZJYuOEH4MpphTIiarpYoBA5QWxkIML8vVDTtgkBQJi/F2IjAx0aP8TPs15jhyAPwzQHJO0+M91bfiVVmdSWjxJqW0e1tfnSPAD5ord1WoAIpPDCbUSuxgKFyAm0GgFzhkcBqP4FXTE9Z3gUtBrHvr5fvr9zvcZ+vMqF2W6IeqwzD3Ro2bWpKR8l1LWOampTCG98ab5H2unwGqDYKHuORFQzFihETjI0OgwrxvdAsJ+nJB7q74UV43tgaHSYw2MPjgqxf+yyIjym3SVp9x/zXbgGX8hpyZgYm/ksGRMj63LsVdM6CvHzqrPNdu8RMIuVypbS68Chz5yaLxFJsUAhcqKh0WH4PvFu6/TqSb3x00sDG1Sc1Hds3a8b0EKQXrb9kyrHWchhcFSIzXwGR4XIvix72VpHOxP/VGebz174M5IsvaWDHXgfMPPCbUSuwgKFyMkq72qJjQx0eLeOQ2NbLNVPLe4Qj9Nia9lyqFc+CrAnJ1ttPq5yyjGM54Fj25yWJxFJsUAhcmenvocm75QkVBY7RaFkGpdD4q1Is1S5wu7+fymTDFETpFM6ASJyon3vSSYtrTrBEjEAwA6XpeCj1yFr4TDrdFFp9d0kVdvItayGEfCJ6T4s1Vdah+cPAL+nArf0lGkZRFQTbkEhclc56UCm9MZ4ZbwwW718Y4mFxTdcGty/XJlkiJoYFihE7qrK7ojLoh/M0X9WKJnGyQQdTL3+Kg3+tgkwXlAkH6KmhAUKkTsqMAC/fiUJfW4aDOicd+Vad2WKeRzw8LkZEM28cBuRC7BAIXJHBz8CzKXWyRLRA2vM8Qom1Ih5twC6jZXGUleVXxuFiJyGBQqRuym7AfzysSS00dwfV+CvUEJuoO8z0uliI3RH1imTC1ETwQKFyN38bwNQdEUS+th8fw2NyS6tOgK33ScJ6Q6+Dw0sCiVE5P5YoBC5E1EE9kkPjjVH3oOT4i0KJeRG7pwmmdRcy8K9ml8USobI/bFAIXInp5KBy8clobI+z9TQmOqlXX8gLEYSekq3XZlciJoAFihE7mTfMul0UCdYIu+x3ZbqRxCAO/8mCfXUnEQP4YRCCRG5NxYoRG5CyPkfcGa3NNiXF2aTVdSDgJ90d9lfdd8olAyRe2OBQuQmPA5UucJpsyCg6xhlknFXWg+gr/ReRkM0ByFczVImHyI3xgKFyA20xiVoMzZKg7FPAx68MJvsejwO6H2tk1pBhO7gylo6EJEjWKAQuYEndd9CEM03Ax4+QO8nlUvInXn5Az0nSEK6I+uAG1cVSojIPbFAIWrk/HAdj2p/kAZ7PA74BCqTUFPQZwpEQWudFMoKy6/eS0SyYYFC1MiN136PZkLJzYCgrX7lU5JXQBuYOz8oje1fWX4VXyKSBQsUosbMVIxJuu+ksTtGAi3aKZJOU1LWV3rKMYouQ/c/Xv6eSC4sUIgaMe2vXyJIMEqDdz6rTDJNjBjaFXvMXSUx3f73oIW5hh5EVB8sUIhUwGwRrb+nZOZJpmtsYzZXP7U48m4gPKbWfqUmi2TabBHtakNSZouIFeYRkpjGeA7DNPsB1ON15Lolskn2AmXBggXo3bs3fH19ERwcjJEjR+L4cemlt4uLi5GQkICWLVuiefPmGD16NAwGg9ypEDUKSenZiF+8xzo9cdVBxC3ahaT07Frb/H3BQmjyTkkH6y/demKrX4/Xd0qme76xE3ct+qHWNnGLdmFnBv9GK1Ss1/2Wzjhs6SiZN1W3BYBo9+tYtQ0RlZO9QNmzZw8SEhKwf/9+7Ny5E2VlZbj33ntRWFhobfP8889j69at+Oqrr7Bnzx5cvHgRo0aNkjsVItVLSs/G1DWHYMgvkcRzjMWYuuYQktKza2gjYlzpl5I+luA7gA6D6hy76j/s14rKcO1GWa1tcozFmL4+rV7PzV1J16uAFabhkvmdNedxjyYNgD2vo7QNEd2kk3vApKQkyfTq1asRHByM1NRU/OlPf4LRaMTHH3+MdevWYeDAgQCAVatWoXPnzti/fz/69u0rd0pEqmS2iJi7NQO2NvCLAAQAr235DYBQrc0AzRF00WRJYmX9noPnH5e1r21sR1Tk09TZWq87LT1xyhKOjpqL1thU3Rb8UNq9ztcRuLlu527NwOCoUGg1XNNEgAuOQTEayw/gCwwsvyZDamoqysrKEB8fb23TqVMntG3bFvv27bM5RklJCfLz8yUPosYuJTMP2cbiGueLAHLyS5CTX7WNiL/ppFeNPWMJhbnzSLvHdgSPlLC9XkVosNIs3YoSqzmOXsKxP+bX9DpWHgPINhYjJTNP7pSJGi3Zt6BUZrFYMH36dPTv3x/R0dEAgJycHOj1egQEBEjahoSEICcnx+Y4CxYswNy5c52ZKqlQUakJUbPLT6HNmDcEPnr53q5yjd2QcXILHCsg+mky0FNzUhJbYR6BuRqtJB9n+eeYGOvz9NHrkLVwmHVeUampzv5V+9jL0X6OjFNTm5pes83m/nhe9zVaC1essam6rXiyrFO9cnP0PdEQtt7DzvzbI7KXU7egJCQkID09HevXr2/QOLNmzYLRaLQ+zp8/L1OGRMoJ9nXsPjkJ2k2S6d/FVthojpMhI/s4mrc7qOm5l0GHj0z3S2KDtIcRLZyRZXyipshpBcq0adOwbds2/PDDD7jllpu3Jw8NDUVpaSmuXbsmaW8wGBAaGmpzLE9PT/j5+UkeRI1dbGQgwvy9ajy2Q7Dxew/hBOK0v0narTCNgMnGxtAQP09ZjxsRAIT5eyE2suleQr+212y9+R7kic0lsed0/4EAINTPE6F+tb/WTX3dElUle4EiiiKmTZuGjRs3YteuXYiMjJTM79mzJzw8PJCcnGyNHT9+HOfOnUO/fv3kTodItbQaAXOGRwGofgCqrS8yAUCCbrMkliO2wNfmP9kc/+X7O9c4Vn1VjDFneFSTPoizttfsBrzwgekBSWyw9hCihTN4bcQdeG1E7a91U1+3RFXJXqAkJCRgzZo1WLduHXx9fZGTk4OcnBzcuFF+jwp/f388+eSTSExMxA8//IDU1FRMmjQJ/fr14xk81OQMjQ7DivE9EOznKYmH+nthyZgY6/SSMTGIa34Bg7SHJe0udXkaJdDbHHtwVIjNsat+Bwb4eCDA26PWNqH+XlgxvgeGRofZ8azcW02vWYCPBzZ73F9tK8rHEckYGh1W62vNdUtUnexHPq1YsQIAMGDAAEl81apVmDhxIgDg3XffhUajwejRo1FSUoIhQ4bgX//6l9ypEDUKQ6PD0L9jK3R5bQcAYPWk3rjr1iCUmG5eMn1wVAjuz9gDnLjZT/RphQ5DnwF++aleY8e0CUDMvJ2SZRWVmupsw//ub6rpNSsqNWH56w9gpsfN4+6Cs38ALh4GwrvX2I/rlqg62QsUUaz7ZEQvLy8sX74cy5cvr7MtUVNQ+QsqNjKw2heWkJ0GjxPbpLF+CYC+Wb3HrqxiWfa0ISlbr5lWI+Az872YrNuGQOH6zca7FwGPra+xHxFVx3vxEDUC+j3zJdOiVwDQ+6/KJEO1KrJxLApOfFu+FYWI7MYChUjlYoWj0J5JlsTK+j0HePFsNrX6zHwvRO+W0uDuRcokQ9RIsUAhUjUR/+chvedOrhgAUy9uPVGzInihrO80afDEt8CFQ8okRNQIsUAhUrEBmiOI1UjvBr7U9BDg4aNQRmQvU88nAJ8qW1G+fw2w4zg9ImKBQqReogX/p5NuPTlvCcIG8z0KJUT1om8O3PmsNJa5B5rMH5TJh6iRYYFCpFLaY1sRXeWOxUtMo1Hm3FtokZz6PA34tZaE9LtegwYWhRIiajxYoBCpkdkEjypn7lha3oaNFtfdc4dk4OEN3PN3SUiT+xse0tR87RoiKscChUiN0tZAk3dKEiq7exYs/JNtfLqNAYLvkIRe8PgSnihVKCGixoGfdkRqU5wPJL8uCVlCu8F8+3CFEqIG0WiBwXMloXAhD5O0SQolRNQ4sEAhUhmP/74DFF2WxErvmQ0IvOJoo9UxHoiU3tTxGd1moOiKQgkRqR8LFCIViRSyoTv4gSS2w9wTlsgBiuRDMhEEYPA8SchPuAGP/y5WKCEi9WOBQqQif9etgWAps06Xilq8aRqnYEYkm/DuQJeHJSFd6kfApeM1dCBq2ligEKnEnzRHEK+V3q/lE/P9OCuGKpQRyW7gKxC1euukYDEB21/gxduIbGCBQqQG5jK8qlsjCYnNgvGe6UGFEiKnaBEBU+xUaSzrR+DXr5XJh0jFWKAQqYDu0Ce4VXNBEisd8Aqug5e0dzdl/V/A72IraXDH34FiozIJEakUCxQipeVnw2PvQmksLAbmrmOVyYecS98M88r+Io1dNwA/zLfdnqiJ4jWziZzMR69D1sJhtmeKIrA9EUJJvjR+3yJAqPv/h6pjF5WaGpJqg9X6XBViT06uznuHpReSzd0xqPIxRykfADHjgLCuLsuDSM24BYVISb9tBI5/IwmZujwKtO2rUELkGgJeMz0OUed1MyRagO2JgIX36SECWKAQKacoD/h2hiR0SfRD6aA3FEqIXOm8GIKyO5+XBn8/CKSuUiYhIpVhgUKqZbbcPPUyJTNPMq2WsRs0zncvA4WXJKE5ZRMBn0CHx3bmOiP5mfpOAwLbS4M7XgXyzrgsB1vvGb6PSA14DAqpUlJ6NuZs+c06PXHVQYT5e2HO8CgMjQ5TxdgNGufk98CRLyShHeae+MbSB+/UMHaIn6dD+cy8r5Pdz4lcTOcF3P8OsGbUzVhZIbBxKjDpm/L7+DiRrfdMgI8HUKkekfNvj6g+uAWFVCcpPRtT1xyCIb9EEs8xFmPqmkNISs9WfOwGjVNSAGybLgmJnn54pewJAAJ2Zhhsjp1bZdrefKavT7PnKZFSOg4Cek6Uxs7vB35e5tTF1vSeuVZUhms3yiQxOf72iOqLBQqpitkiYu7WDNjaoFwRm7s1w6FNznKN3eBxvpkBGM9LQqWD5iEXLQAA8785WuvYFTnUJx9SuXvfBFpESGM/vAnkpDtlcbW9Z2xp6N8ekSNYoJCqpGTmIdtYXON8EUC2sRgpmXmKjd2gcdLWAUfWSWMRd8Hcbbx1sup/tLaknr1ar3xI5TybAw+9D6DSHavNpcDGpwFT3e+H+qrrPWNLQ/72iBzBAoVUJbfAvg9Ne9s5Y2yHx7l0ovy+K5XpfYERy8rvdlsPlwpufmk5si5Ihdr2Bfo/J40Z0p1yAbeGvGf4fiNXYYFCqhLs61V3o3q0c8bYDo1TdgP4aiJQViRtNGIpEBhp13iVBfnePGDWkXVBKnXPy0BItDT2338CJ76TdTENec/w/UauwgKFVCU2MhBh/l6oaXuCACDM3wuxkYGKje3QOEmzgNzfpA17TgKiR6GqED/PGse2dm3Xol75UCOh8yzf1VPpjseACPz7KeDKadkWU9d7xpaG/O0ROYIFCqmKViNgzvAoANW/WCum5wyPglZT/69ducau9zjp/6l+8a3gO4ChC2yO//L9nWsduyKH+uRDjUhoNBD/mjRWYgTWP1Z+BpgManvP2NLQvz0iR7BAIdUZGh2GFeN7ILjKdT9C/b2wYnyPBl2LQa6x7R4n+wiweZq0s4cP8PAqwMPb5tiDo0Jsjh3iV/Om9dryWTImxq7nRCrS9xkgerQ0dukYsOmZ8vs3yaCm90yAjwcCvD0kMTn+9ojqixdqI1UaGh2G/h1boctrOwAAqyf1xl23Bsny35tcY9c5jvF3YO0j5RfeqmzYP4Cg2+s9ds92LazT9cmnxGSu1/MiFRCE8oOnLx0vP1C2wtEtwE/vAnclyrKYmt4zRaUmp/ztEdUHt6CQalX+QIyNDJT1A1KusWscpzi/vDi5niPtEDMOiHnMaTk6c52Ri+mbAY9+DngFSOPJ84DjSbItxtZ7hu8jUgMWKERyM5cBX02oflBsuzjggXeVyYkap8D2wJ8/hvRIEbH8/ZX1X6WyInIJFihEchJFYHsicHqXNN7qNmDMmvKzNIjqo2M8MGi2NGYqBr4YA1xMUyQlIldggUIkF1EEvp8DHPpMGvdpBTz2JeDdwnY/orrEPQ90GyuNleSX32Tw0gllciJyMhYoRLIQ4fH938svqlWZzgt4bINDF2MjshIEYMR7wO3DpPGiK8BnDwJXzyqTF5ETsUAhaiABFryuWwWPg+9Xm4NRHwK39FIkL3IzWh3w50+AyD9J4wUXgc9GAHlnlMmLyElYoBA1hMWMBbqP8Bfd99K4oAFG/guIGqFMXuSePLyAMeuA1lWK3qtZwEeDgQuHFEmLyBl4HRQiR5XdgH5LAsbodkvjghYY9QHQ5c92D+Wj1yFr4bAGt7G3X9Vpe9qQlK115szX0crTFxj3FbB6GJCbcTNedBlY/QDwyGfArfGOj0+kEtyCQuQI4+/Aqvugy/i3NK75YzN8PYoTonrzCQT+sgkI6SKNlxUCXzwKpK1TJC0iObFAIaqvs/uADwYAFw9LwqLGo/y/1ztGKpIWNTG+IcCkb4DIu6VxiwnYNBXY8SpgKlUmNyIZsEAhspcoAgc/Bj59ACi8JJl1XfRCycNrgE7cLUIu5OUHjPsa6PJw9Xk/LwVWDQXyMl2fF5EMWKAQ2aPAAGwYX34RNotJMivTEoKHSufB0oH7/UkBOj3w0AfAnc9Wn3chFXj/T0D6v6vPI1I5FihEtRFFIO0LYHkscGxbtdnm9oPwYOnrOCneokByRH/QaIB7XwfufwfQ6qXzSvKBr58ovxNy4RVl8iNyAAsUopoYfwfWPQJsmgIUX6s+v/90lDzyBfLR3OWpEdkU+xTw5E4gsEP1eWlrgWU9gJQPAbOp+nwilWGBQlRVUR6w4xVgWU/g5I7q831alh8MO3guoNG6Pj+i2oTHAE/vAbqOqT6v+Brwzf+VH+R9dp+LEyOqH14HhahCSQGw71/Az8uA0gLbbaJHA/e9BTRr5drciOrD0xcY9T7QfkB5QVJ6XTrf8Gv5AbQdB0PT9zlFUiSqCwsUavIE43ngyGfAoU/L721iS/MQYNhioPMDrk2OqCFixpZfGn/nq7YPlD21E16nduIr/W1YYRoBiPe6PkeiGrBAoaZJtOBuzRGM1+6E17/SANFiu51GB/SYAAx8pfziWESNjX/r8osH9pwEfDtDevXZP/TWnEBv/TuwvL8RiBkLoTMvNEjKY4FCTYfZBJz9L3B0C7yObsWnekN5XLTVWCi/tsQ9s4DA9q7Mksg5Iu8Cnv4ROPgRsPctm1sLNXmngV1vwHvXG1jnEYWNljigsAegD1MgYWrqWKCQe7t6Fjj7M5D1I3D8W+BGHoA6jg6/7b7yLSah0S5JkchltDqg7xSgx+PA4c/Lj7cynrfZ9E5tBu7UZgD//AAI7w50jAc6DgZa9ywfh8jJ+C6rxGwRkZKZh9yCYgT7eqFnuxZIPXvVOh0bWb6J3xltnDl2Y86xtten6jhhnmXo6X0RQs6vuHJiH5pl74fPjWy7XnvR0w85EQ8h45Y/wyf8DsQGBwJ2vh/qylGrERxuR42bo+8Hp/9dn7+BXM8HEPLAg+h27Xuc3zoft2ku1PxELh4uf+x9GyatN4padkGzDn1wyqMTsrw6wS84ArHtW8qbYyP4fGoqOSr1+aRogbJ8+XK8/fbbyMnJQbdu3bBs2TLExsYqkktSejbmbs1AtrHYGtMIgKXS5v8AHw8AwLWiMtnbOHPsxphjmL8XZt7XyTq9M8OAhd8ewxVjAUKFPLQTDNjleRm3iAaEmS8iRjiPdppca/sg2CfD0g6l3Sci8ehtOHNEAI4UANjfoBwrv4fC/L0wZ3gUhkbf3ERu671mqx01bva+zkp/9oT4hcJQ+hZ6CCexoW8WPI5uBIqNNT4vnfkG/HJTgNwU3A7gdgAFojcyhNbIRGtklIXhrBiCi2JLZCMIl0RfiH9ss3SXz6emlqNSn0+CKIo298A724YNG/D4449j5cqV6NOnD5YsWYKvvvoKx48fR3BwcK198/Pz4e/vD6PRCD8/vwbnkpSejalrDtk+FIFkp4MJXiiFF8rgIxSjOW6gGYrRTCiGL4rgLxQiEAXwFwoRIBQgCEYEC9cQIlxFC+F63Quow6+WCCSZY5Fk6Y3TYmsIqOEwlDpU/D8hVpquOk5FmxXje2BodFiN77Wq7QCgqNSEqNnfAQAy5g2Bj77u/ycc6UPys/d1VsNnT+X3bca8IfARTCjJ+Aa7v16O/pp0NBeKa+tepxLRA9liIC7DH1dEP1wR/XAZfjCKzZEPH+SLPiiADwpEHxTBE0WiF4rgiRvwRCl0uLnWSCm2Pp8cVZ/vb8U+vRYvXoynnnoKkyZNAgCsXLkS27dvxyeffIKZM2e6LA+zRcTcrRnor/kVwzT7Xbbc+hDs+Piq+idsq48AEYJQeb5o7StYfxet0zd/t0AD0fqomNbCAs0fv+sEM7SwQAcztDBDBzP0MMEDJngIZuhRBj1M8EYJdEINZ8w4SZ7YHCmWzthv6YzvLT3xuxgk+VB29MtBhHS92xqnos3crRkY2CkEc7dm1NlucFQod/c0YhWfKQ15P7hS5eWbLSLg5QVz5xF4uswTHjBhcPMz6FZ8EHdr/odOGtvHq9TGUyhDhGBABAz17msRBZTAA6XQoQR6lEKHMlELE3QwQYsyaGGCFmZoyj95RA3Mf3wqlf8U/viUqvhEwx+fYrB+wlX9veKvWoQAW/++i1U+batOl8fqZqufGh0WO+JL8z2KfD4pUqCUlpYiNTUVs2bNssY0Gg3i4+Oxb1/1qxuWlJSgpKTEOp2fny9bLimZecg2FuN+7XmM1f0g27ikjDJRi1NiOI6K7XDY0hEHLJ1xUmxt3cRcQa4vBfs+iIBsYzE+35cl2YxfU7uUzDz069BSpgzJ1So+U2pi7/tBCalnr2LA7Te3YJdBh2+u34ZvcBsWYBxawohumtOI0ZxCjHAaMZrT8BOKnJaPRhDhjVJ4oxTAH8tpHN/rbsPTXIYvzfco8vmkSIFy+fJlmM1mhISESOIhISE4duxYtfYLFizA3LlznZJLboG6PiDIPkbRB2fFEJwTQ3BWDEamGIYMSzucElujFB5Kp2fT2Tz7Psgr3pM+eh2yFg6r1zIc6UPysvczxd73gysZb5Qfd1DxPtqcdgHPrU+zzr8Cf+yy9MAuS48/IiLCkIeOmgu4VbiAjsIFdNBcRDiuIFTIg4dgdv2TIKdy5Xdmo9hBPWvWLCQmJlqn8/Pz0aZNG1nGDvb1kmUccpxZFFAILxTCG9dFb1xDM1wTfXFNbIar8EWe6AuD2AIGtECuGIBcsQXy0UzptOutXaCPXe34nmzc7H397H0/uFLV3Ot+LgKy0RLZlpb4EV0lczSwIAjX0Fq4jFAhD4FCAVoJRgSi/KcfCuEr3LD+9EURPAXexFDtXPn5pEiB0qpVK2i1WhgM0n2SBoMBoaGh1dp7enrC09PTKbnERgYizN8LvxVE4BPTUKcsozaO7oes7z5O0bpfteq8m/GKmMXGvlmLWLE/t/yoFDNu7uu1/DFt+mN/sEks3ydcCg+UQYsy6FAGHUpFHYrhiWJ4oFjUoxh6FMILxdDD1nZbAUCInycAAYb8Yln31cs1tj3jCABC/b3wl34R+OinTOQYa29n6/RlajwqPlPqep3rej+4Uk3vvbqeS20s0MCAQBjEQLv3qWphhg9K4I0S+AjF8EIZ9CiDJ8qgF8p/evxxjJsOJugFE7Sw/PEwW3/XVDpuTvjjeLnyY/DKpzW4edTJzWPuKtw8Hq/y+qkaszVduW1D2XPsoSv8z1J+oUolPp8UKVD0ej169uyJ5ORkjBw5EgBgsViQnJyMadOmuTQXrUbAnOFRmLqmGAcsUSp5S1DFH/lrI+4AAExdc8jhs22cNbY941S0mTM8Cnqd5o/3Wu3teIBs43bzM8Xx94Mr1fbeq+25OIMZ2vIzeuBTfWH8cFaMUp9PtV5Q05kSExPx4Ycf4tNPP8XRo0cxdepUFBYWWs/qcaWh0WFYMb4HQv2lm66qvg4BPh7Wc8TlbuPMsRtjjqH+XtZT2mp6fRzN0dGxHRmnchug5vda1XbUuDX0/eDKv+u63ntqyFFtn09NLUelPp8Uuw4KALz33nvWC7XFxMRg6dKl6NOnT5395L4OSgVeSVZdOVat1Ou6kqyzx3Z0HF5JtulS7ZVkHXjvKZmjGj+fmlKOcn4+1ef7W9ECxVHOKlCIiIjIeerz/a3YLh4iIiKimrBAISIiItVhgUJERESqwwKFiIiIVIcFChEREakOCxQiIiJSHRYoREREpDosUIiIiEh1WKAQERGR6ihys8CGqrj4bX5+vsKZEBERkb0qvrftuYh9oyxQCgoKAABt2rRROBMiIiKqr4KCAvj7+9faplHei8diseDixYvw9fWFIDTdG6zl5+ejTZs2OH/+PO9J5GRc167Dde06XNeuw3VdThRFFBQUIDw8HBpN7UeZNMotKBqNBrfccovSaaiGn59fk37DuxLXtetwXbsO17XrcF2jzi0nFXiQLBEREakOCxQiIiJSHRYojZinpyfmzJkDT09PpVNxe1zXrsN17Tpc167DdV1/jfIgWSIiInJv3IJCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKG6mpKQEMTExEAQBaWlpSqfjdrKysvDkk08iMjIS3t7e6NChA+bMmYPS0lKlU3Mby5cvR0REBLy8vNCnTx+kpKQonZLbWbBgAXr37g1fX18EBwdj5MiROH78uNJpNQkLFy6EIAiYPn260qmoHgsUNzNjxgyEh4crnYbbOnbsGCwWC95//3389ttvePfdd7Fy5Uq8/PLLSqfmFjZs2IDExETMmTMHhw4dQrdu3TBkyBDk5uYqnZpb2bNnDxISErB//37s3LkTZWVluPfee1FYWKh0am7t4MGDeP/999G1a1elU2kcRHIb33zzjdipUyfxt99+EwGIhw8fVjqlJuGtt94SIyMjlU7DLcTGxooJCQnWabPZLIaHh4sLFixQMCv3l5ubKwIQ9+zZo3QqbqugoEC89dZbxZ07d4p33323+NxzzymdkupxC4qbMBgMeOqpp/D555/Dx8dH6XSaFKPRiMDAQKXTaPRKS0uRmpqK+Ph4a0yj0SA+Ph779u1TMDP3ZzQaAYDvYydKSEjAsGHDJO9vql2jvFkgSYmiiIkTJ2LKlCno1asXsrKylE6pyTh16hSWLVuGd955R+lUGr3Lly/DbDYjJCREEg8JCcGxY8cUysr9WSwWTJ8+Hf3790d0dLTS6bil9evX49ChQzh48KDSqTQq3IKiYjNnzoQgCLU+jh07hmXLlqGgoACzZs1SOuVGy951XdmFCxcwdOhQPPzww3jqqacUypyoYRISEpCeno7169crnYpbOn/+PJ577jmsXbsWXl5eSqfTqPBS9yp26dIlXLlypdY27du3xyOPPIKtW7dCEARr3Gw2Q6vVYty4cfj000+dnWqjZ++61uv1AICLFy9iwIAB6Nu3L1avXg2NhrV+Q5WWlsLHxwdff/01Ro4caY1PmDAB165dw+bNm5VLzk1NmzYNmzdvxt69exEZGal0Om5p06ZNeOihh6DVaq0xs9kMQRCg0WhQUlIimUc3sUBxA+fOnUN+fr51+uLFixgyZAi+/vpr9OnTB7fccouC2bmfCxcu4J577kHPnj2xZs0afrjIqE+fPoiNjcWyZcsAlO9+aNu2LaZNm4aZM2cqnJ37EEURf/vb37Bx40bs3r0bt956q9Ipua2CggKcPXtWEps0aRI6deqEl156ibvVasFjUNxA27ZtJdPNmzcHAHTo0IHFicwuXLiAAQMGoF27dnjnnXdw6dIl67zQ0FAFM3MPiYmJmDBhAnr16oXY2FgsWbIEhYWFmDRpktKpuZWEhASsW7cOmzdvhq+vL3JycgAA/v7+8Pb2Vjg79+Lr61utCGnWrBlatmzJ4qQOLFCI6mHnzp04deoUTp06Va3448bIhnv00Udx6dIlzJ49Gzk5OYiJiUFSUlK1A2epYVasWAEAGDBggCS+atUqTJw40fUJEdnAXTxERESkOjyyj4iIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6LFCIiIhIdVigEBERkeqwQCEiIiLVYYFCREREqsMChYiIiFSHBQoRERGpDgsUIiIiUh0WKERERKQ6/w/u4tLW50YjggAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "from numba_stats import norm\n", "from iminuit import Minuit\n", "from iminuit.cost import ExtendedUnbinnedNLL\n", "import matplotlib.pyplot as plt\n", "\n", "# generate toy sample\n", "rng = np.random.default_rng(1)\n", "x = rng.normal(size=100)\n", "\n", "# bin it\n", "w, xe = np.histogram(x, bins=100, range=(-5, 5))\n", "\n", "# compute bin-wise density estimates\n", "werr = w ** 0.5\n", "cx = 0.5 * (xe[1:] + xe[:-1])\n", "dx = np.diff(xe)\n", "d = w / dx\n", "derr = werr / dx \n", "\n", "\n", "# define model and cost function\n", "def model(x, par):\n", " return par[0], par[0] * norm.pdf(x, par[1], par[2])\n", "\n", "cost = ExtendedUnbinnedNLL(x, model)\n", "\n", "# fit the model\n", "m = Minuit(cost, (1, 0, 1))\n", "m.migrad()\n", "m.hesse()\n", "\n", "# plot everything\n", "plt.errorbar(cx, d, derr, fmt=\"o\", label=\"data\", zorder=0)\n", "\n", "plt.plot(cx, model(cx, m.values)[1], lw=3,\n", " label=\"fit\")\n", "plt.legend(frameon=False,\n", " title=f\"$n = {m.values[0]:.2f} +/- {m.errors[0]:.2f}$\\n\"\n", " f\"$\\mu = {m.values[1]:.2f} +/- {m.errors[1]:.2f}$\\n\"\n", " f\"$\\sigma = {m.values[2]:.2f} +/- {m.errors[2]:.2f}$\");" ] }, { "cell_type": "markdown", "id": "realistic-trail", "metadata": {}, "source": [ "We want to understand how uncertain the Gaussian curve is. Thus we want to draw a 1-sigma error band around the curve, which approximates the 68 % confidence interval." ] }, { "cell_type": "markdown", "id": "polyphonic-patient", "metadata": {}, "source": [ "## With error propagation\n", "\n", "The uncertainty is quantified in form of the covariance matrix of the fitted parameters. We can use [error propagation](https://en.wikipedia.org/wiki/Propagation_of_uncertainty) to obtain the uncertainty of the curve,\n", "\n", "$$\n", "C' = J \\, C \\, J^T,\n", "$$\n", "\n", "where $C$ is the covariance matrix of the input vector, $C'$ is the covariance matrix of the output vector and $J$ is the matrix of first derivatives of the mapping function between input and output. The mapping in this case is the curve, $\\vec y = f(\\vec{x}; \\vec{p})$, regarded as a function of $\\vec{p}$ and not of $\\vec{x}$, which is fixed. The function maps from $\\vec{p}$ to $\\vec{y}$ and the Jacobi matrix is made from elements\n", "\n", "$$\n", "J_{ik} = \\frac{\\partial y_i}{\\partial p_k}.\n", "$$\n", "\n", "To compute the derivatives one can sometimes use [Sympy](https://www.sympy.org/en/index.html) or an auto-differentiation tool like [JAX](https://jax.readthedocs.io/en/latest/) if the function permits it, but in general they need to be computed numerically. The library [Jacobi](https://github.com/hDembinski/jacobi) provides a fast and robust calculator for numerical derivatives and a function for error propagation." ] }, { "cell_type": "code", "execution_count": 2, "id": "technological-justice", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from jacobi import propagate\n", "\n", "# run error propagation\n", "y, ycov = propagate(lambda p: model(cx, p)[1], m.values, m.covariance)\n", "\n", "# plot everything\n", "plt.errorbar(cx, d, derr, fmt=\"o\", label=\"data\", zorder=0)\n", "\n", "plt.plot(cx, y, lw=3, label=\"fit\")\n", "\n", "# draw 1 sigma error band\n", "yerr_prop = np.diag(ycov) ** 0.5\n", "plt.fill_between(cx, y - yerr_prop, y + yerr_prop, facecolor=\"C1\", alpha=0.5)\n", "\n", "plt.legend(frameon=False,\n", " title=f\"$n = {m.values[0]:.2f} +/- {m.errors[0]:.2f}$\\n\"\n", " f\"$\\mu = {m.values[1]:.2f} +/- {m.errors[1]:.2f}$\\n\"\n", " f\"$\\sigma = {m.values[2]:.2f} +/- {m.errors[2]:.2f}$\");" ] }, { "cell_type": "markdown", "id": "final-sensitivity", "metadata": {}, "source": [ "Error propagation is relatively fast." ] }, { "cell_type": "code", "execution_count": 3, "id": "continent-astrology", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.96 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1,000 loops each)\n" ] } ], "source": [ "%%timeit -r 1 -n 1000\n", "propagate(lambda p: model(cx, p)[1], m.values, m.covariance)" ] }, { "cell_type": "markdown", "id": "advance-flight", "metadata": {}, "source": [ "## With the bootstrap\n", "\n", "Another generic way to compute uncertainties is bootstrapping. We know that the parameters asymptotically follow a multivariate normal distribution, so we can simulate new experiments with varied parameter values." ] }, { "cell_type": "code", "execution_count": 4, "id": "duplicate-community", "metadata": {}, "outputs": [ { "data": { "image/png": "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K664go0bNwYt5gulBEVERMLGq6++yvz581m4cCGbNm1izJgxzJw5k6KiogvaZ+PGjeTn5/sf7733HgBf/epXzxvT9OnTWbp0acDvwev18uabb3LTTTcFvE9L2uuY/Md//Afvvfcef/jDH9i2bRvXX389M2bM4OjRo0GJ+4K1aSi4ENNIsiKikWQD989//rPRcPcHDx40gMnNzQ1hZI1NmjTJzJkzx7/s9XpNZmamWbx4cVD3+c53vmMGDRpkHMc5b0yBjCJb34cffmgyMjICqjsQ7XFMysvLjcfjMW+++aZrv3Hjxpn/9//+X1Dibkprvr/VgiIi0sVt2bKF4cOHu2bY3bx5Mz169KB///6uso899hhxcXEtPg4fPtwucVZVVZGdnc2MGTP862zbZsaMGaxduzao+yxbtoxvfOMbWJYV3DeBb0bjWbNmBaXu9jomNTU1eL3eRrMux8bG8tFHH11w3MGggdpERLq4rVu3Njmb8ZgxYxqVve+++7jttttarK8tsxkH4vjx43i9XtLS0lzr09LS2LVrV9D2ef311zl9+jR33313UOJuaPny5fzyl78MSl3tdUzi4+OZMmUKP/nJTxg+fDhpaWn88Y9/ZO3atQwePDgosV8otaCIiHRxW7ZsYezYsa51mzdvbrQOIDk5mcGDB7f4iIho+X/bBQsWNNkhtf6juS/XjvC73/2Oz3/+880mWg1bkf75z39y3333BdSKtHPnTvLy8rj22msbbQu34/KHP/wBYwy9e/cmOjqaZ555hjvuuAPbDo/UoNVRfPjhh8yaNYvMzEwsy+L11193bTfG8PDDD5ORkUFsbCwzZsxg7969rjInT55k9uzZJCQkkJSUxDe/+U3OnDlzQW9EREQa83q9bN++vVELyqZNm5pMUIJxiee73/0uO3fubPExcODARvv16tULj8dDYWGha31hYSHp6elNvlZr9zl06BD/+Mc/+I//+I9m47/vvvvYsmWL/zFhwgQeeeQR17rmkps33niD6667rtGlk7Yel/Y8JoMGDWLNmjWcOXOGI0eOsGHDBqqrq5v83YRCqy/xlJWVMWbMGL7xjW9wyy23NNr+5JNP8swzz/Diiy+SlZXFj370I2bOnElOTo7/FzZ79mx/L+rq6mruuece7r33Xl5++eULf0ciIuK3e/duKioqXF+oa9eu5ejRo00mKMG4xJOSkkJKSkqrY42KimL8+PGsWrWKm2++GQDHcVi1ahVz584Nyj4vvPACqamp3Hjjjc3GkZycTHJysn85NjaW1NTUgC59LF++nHvvvbfJbW05Lh1xTLp370737t05deoU77zzDk8++WSrYmw3F9IbFzCvvfaaf9lxHJOenm5+9rOf+dedPn3aREdHmz/+8Y/GGGNycnIMYDZu3Ogv8/bbbxvLsszRo0cDel3dxSMiuosnMC+99JIBzNy5c82ePXvM3//+dzN48GADmA0bNoQ6vEZeeeUVEx0dbZYuXWpycnLMvffea5KSkkxBQYExxpglS5aYa665plX71PJ6vaZfv37m+9//fqtiCvQunsLCQhMZGWmOHTvWqvrPJ5D31/C4BLLPypUrzdtvv20OHDhg3n33XTNmzBgzefJkU1VVFdT462vN93dQO8kePHiQgoICV8/hxMREJk+ezNq1a7n99ttZu3YtSUlJTJgwwV9mxowZ2LbN+vXr+fKXv9yo3srKSiorK/3LJSUlwQxbRKTL2rJlCzNnzuTAgQOMGjWKESNGsGjRIu6//36eeeYZ/vCHP4Q6RJevfe1rHDt2jIcffpiCggLGjh3LypUr/R0+jx8/zv79+1u1T61//OMfHD58mG984xvtEvuKFSuYNGkSvXr1Cmq9gby/hsclkH2Ki4t56KGH+Oyzz0hOTubWW2/lpz/9KZGRkUGNv60sY4xp886WxWuvveZvQvr444+ZOnUqeXl5ZGRk+MvddtttWJbFq6++ymOPPcaLL77I7t27XXWlpqb6/2ga+vGPf8yiRYsarS8uLiYhIaGt4YtIJ1ZeVcOIh98BIOeRmXSL0k2JTZk5cyYTJ07k0UcfDXUoXd5NN93EtGnTePDBB0MdStgqKSkhMTExoO/v8Oiqex4PPfQQxcXF/seRI0dCHZKISKewdetWRo0aFeowLgrTpk3jjjvuCHUYXUZQ/+Wo7R1cWFjoakEpLCz0d8ZKT09vNDxvTU0NJ0+ebLZHcnR0NNHR0cEMVUSkyysoKKCwsFAJSgdRy0lwBbUFJSsri/T0dFatWuVfV1JSwvr165kyZQoAU6ZM4fTp02RnZ/vLvP/++ziOw+TJk4MZjojIRS09PR1jDCNGjAh1KCKt1uoWlDNnzrBv3z7/8sGDB9myZQvJycn069ePefPm8eijjzJkyBD/bcaZmZn+firDhw/nhhtu4Fvf+hbPP/881dXVzJ07l9tvv73dRicUERGRzqXVCconn3zC1Vdf7V+eP38+AHfddRdLly7lwQcfpKysjHvvvZfTp08zbdo0Vq5c6Rq05qWXXmLu3Llce+212LbNrbfeyjPPPBOEtyMiIiJdwQXdxRMqrekFLCJdk+7iEel8utxdPCIiInJxUYIiIiIiYUcJioiIBMWzzz7LgAEDiImJYfLkyWzYsKHF8l6vlx/96EdkZWURGxvLoEGD+MlPfkL9ngc//vGPG834O2zYsHZ7D/fccw8//OEPg1pna49LoPscPXqUO++8k549exIbG8uoUaP45JNPghp7KClBERGRC/bqq68yf/58Fi5cyKZNmxgzZgwzZ85sNO5VfU888QTPPfccv/rVr9i5cydPPPEETz75JEuWLHGVu/TSS8nPz/c/Pvroo4Djmj59OkuXLg2orNfr5c033+Smm24KuP7zactxCWSfU6dOMXXqVCIjI3n77bfJycnhF7/4BT169Aha7CHXbjMCtSNNFijStQUyEaAmC2ydtWvXmmuuucYkJycbwPUIxrl00qRJZs6cOf5lr9drMjMzzeLFi5vd58YbbzTf+MY3XOtuueUWM3v2bP/ywoULzZgxY9ocV6AT/RljzIcffmgyMjKM4zhtfr2G2nJcAtnn+9//vpk2bVrQ4uworfn+VguKiEgXt3XrVqZPn85ll13GP//5T1auXElycjLXXnstr776qutuiscee4y4uLgWH4cPH3bVX1VVRXZ2tmuiWNu2mTFjBmvXrm02riuuuIJVq1axZ88ef5wfffQRn//8513l9u7dS2ZmJgMHDmT27NmNXj9Y3njjDWbNmoVlWUGpry3HJdB93njjDSZMmMBXv/pVUlNTueyyy/jtb38blLjDhe7LExHp4r797W9zyy238POf/xyAESNGcMcdd5Cdnc1tt93mKnvfffc1WtdQw0E1jx8/jtfrbTR7cFpaGrt27Wq2ngULFlBSUsKwYcPweDx4vV5++tOfMnv2bH+ZyZMns3TpUi655BLy8/NZtGgRV155Jdu3byc+Pj6g9x+o5cuX88tf/jJo9bXluAS6z4EDB3juueeYP38+P/jBD9i4cSPf/va3iYqK4q677graewglJSgiIl1YYWEhH330EWvWrHGt7969e5MtBcnJySQnJ3dIbH/605946aWXePnll7n00kvZsmUL8+bNIzMz0/8lW781ZfTo0UyePJn+/fvzpz/9iW9+85uN6nzsscd47LHH/Mtnz55l3bp1zJ07178uJyeHfv36ufbbuXMneXl5XHvttY3qXLBgAU888USL72Xnzp3t2nm3IcdxmDBhgv+9XnbZZWzfvp3nn39eCYqIiIS/7OxsHMdhzJgxjdZPmDChUfmGX/BNafgF36tXLzweD4WFha5yhYWFzU4CC/C9732PBQsWcPvttwMwatQoDh06xOLFi5v9kk1KSmLo0KGuKVfqa9gCNHv2bG699VZuueUW/7qmplV54403uO6661yjntf67ne/y913393s+wAYOHBgo3VtOS6B7pORkdFojqXhw4fz17/+tcU4OxMlKCIiXZjjOACUlZX5L4l8+umnfPjhhzz66KONyrflEk9UVBTjx49n1apV/nnXHMdh1apVrpaLhsrLy7Ftd1dIj8fjj7kpZ86cYf/+/fz7v/97k9sbtgDFxsaSmprK4MGDW3xPy5cv5957721yW0pKCikpKS3u35S2HJdA95k6dSq7d+927btnzx769+/f6jjDVgd02g063cUj0rXpLp7gKSoqMrGxsebOO+80O3fuNG+++abp37+/mTdvXlBf55VXXjHR0dFm6dKlJicnx9x7770mKSnJFBQU+MssWbLEXHPNNf7lu+66y/Tu3du8+eab5uDBg+Zvf/ub6dWrl3nwwQf9Zb773e+a1atXm4MHD5p//etfZsaMGaZXr16mqKgooLgCuYunsLDQREZGmmPHjrXuTQfgfMel4TEJZB9jjNmwYYOJiIgwP/3pT83evXvNSy+9ZLp162aWLVsW9PcQTK35/lYLiohIF5aSksKf/vQnvvvd7zJ69Gj69evH3Llz/RO9BsvXvvY1jh07xsMPP0xBQQFjx45l5cqVrs6ex48fZ//+/f7lJUuW8KMf/Yj/+q//oqioiMzMTP7zP/+Thx9+2F/ms88+44477uDEiROkpKQwbdo01q1b16YWjeasWLGCSZMm0atXr6DVWet8x6XhMQlkH4CJEyfy2muv8dBDD/HII4+QlZXF008/7epg3NlpskARCTuBTASoyQIlWG666SamTZvGgw8+GOpQujxNFigiIhKgadOmcccdd4Q6DGlA/3KIiMhFTS0n4UktKCIiIhJ2lKCI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FBEREQk7SlBEREQk7ChBERERkbCjBEVERETCjhIUERERCTtKUERERCTsKEERERGRsKMERURERMKOEhQREREJO0pQREREJOwoQREREZGwowRFREREwo4SFBEREQk7SlBEREQk7ChBERERkbCjBEVERETCjhIUERERCTtKUEREAuB1jP/5hoMnXcsiEnztkqAcPXqUO++8k549exIbG8uoUaP45JNP/NuNMTz88MNkZGQQGxvLjBkz2Lt3b3uEIiJywVZuz2fGU2v8y3e/sJFpT7zPyu35IYxKpGsLeoJy6tQppk6dSmRkJG+//TY5OTn84he/oEePHv4yTz75JM888wzPP/8869evp3v37sycOZOKiopghyMickFWbs/n/mWbKCypdK0vKK7g/mWblKSItJOIYFf4xBNP0LdvX1544QX/uqysLP9zYwxPP/00P/zhD/nSl74EwO9//3vS0tJ4/fXXuf3224MdkohIm3gdw6IVOTR1MccAFrBoRQ7XjUjHY1sdHJ1I1xb0FpQ33niDCRMm8NWvfpXU1FQuu+wyfvvb3/q3Hzx4kIKCAmbMmOFfl5iYyOTJk1m7dm2TdVZWVlJSUuJ6iIi0tw0HT5Jf3HzLrgHyiyvYcPBkxwUlcpEIeoJy4MABnnvuOYYMGcI777zD/fffz7e//W1efPFFAAoKCgBIS0tz7ZeWlubf1tDixYtJTEz0P/r27RvssEXkPMqrahiw4C0GLHiL8qqaZte1pZ723K8t9dSWueO36wKqs6hUl6dFgi3oCYrjOIwbN47HHnuMyy67jHvvvZdvfetbPP/8822u86GHHqK4uNj/OHLkSBAjFhG5MKnxMaEOQaTLCXqCkpGRwYgRI1zrhg8fzuHDhwFIT08HoLCw0FWmsLDQv62h6OhoEhISXA8RkY6QlhBNc71LLCAjMYZJWckdGZLIRSHoCcrUqVPZvXu3a92ePXvo378/4Oswm56ezqpVq/zbS0pKWL9+PVOmTAl2OCIiF+QHXxgO0ChJqV1eOGuEOsiKtIOgJygPPPAA69at47HHHmPfvn28/PLL/OY3v2HOnDkAWJbFvHnzePTRR3njjTfYtm0bX//618nMzOTmm28OdjgiIhfkuhFpPHfnOFITol3r0xNjeO7OcdwwMiNEkYl0bUG/zXjixIm89tprPPTQQzzyyCNkZWXx9NNPM3v2bH+ZBx98kLKyMu69915Onz7NtGnTWLlyJTExuo4rIuHnhpEZTB3ci1E/fheApfdM5MohKWo5EWlHQU9QAL74xS/yxS9+sdntlmXxyCOP8Mgjj7THy4uIBF39ZGRSVrKSE5F2prl4REREJOwoQREREZGwowRFREREwo4SFBEREQk7SlBEREQk7ChBERERkbCjBEVERETCjhIUERERCTtKUERERCTsKEERERGRsKMERURERMKOEhQREREJO0pQREREJOwoQREREZGwowRFREREwo4SFBEJLWOgsoR0ThBFdaijEZEwERHqAETk4pPBCSbZO4nYeAhqyoisruL2iH1Umwg8uxyctFGAAaxQhyoiIaIERUQ6TvVZPLlruM2zGssyWGfjwFPXkBtp1WAX7cQu2sk3PUfJdi4BZoYsXBEJHV3iEZEOMcw6TET2/2Lnb8WyzHnLx1tnme7ZgmfP2+A4HRChiIQTtaCISLvyeh2usz/hUjuXvKLeZCTGNCrjmLqE5ejps64ydsGnUHMGhs5y7dMtKoLcx290v5ZTV8+Ggye5ckgKHluXiUQ6IyUoItJuVm7P5++vv8Sldi4Ar285Svco92lnX1EpH+w+5l9uqgynconYsowEelBCXLOvtfCNHf7lu1/YSEZiDAtnjeCGkRnBeUMi0mF0iUdE2sXK7fn87qWXGVixw7W+rKrG/3z/sTO8uS3fta5hmVpW+Qlu93xAAmeafK37l22isKTStb6guIL7l21i5fb8C3krIhICSlBEJOi8juGPy1cw1bO9xXL/3Hv8vHXVv/zTzarki5714NQlMF7HsGhFDk31aqldt2hFjuvyj4iEPyUoIhJ027ZsYEz5+vOWa6qlpKH84grXcqp1Cs++d/3LGw6ebFSmPnOujg0HT573tUQkfChBEZHgKj9J9N43sa3g3HlTVtk4ibELtkHeFgCKSptPTuoLtJyIhAclKCISPMbArjeJiwxeld2jm+nLv/c9KC0gNb7xXUFNCbSciIQHJSgiEjxHNkDxUXonxRLXXGJRT6O7dZrQ1G3JgK8fyo7XmNQnlozEmGbHnLXO1TEpK/m8ryUi4UMJiogER/lJyP0QANuymD405by7XDmk13nL2FYL45icPY1n37ssnDUCaDwwfu3ywlkjNB6KSCejBEVEgsDg2fs2eOv6iwxOjeeLozIatZLE1Vse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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "rng = np.random.default_rng(1)\n", "\n", "par_b = rng.multivariate_normal(m.values, m.covariance, size=1000)\n", "\n", "# standard deviation of bootstrapped curves\n", "y_b = [model(cx, p)[1] for p in par_b]\n", "yerr_boot = np.std(y_b, axis=0)\n", "\n", "# plot everything\n", "plt.errorbar(cx, d, derr, fmt=\"o\", label=\"data\", zorder=0)\n", "\n", "plt.plot(cx, y, lw=3, label=\"fit\")\n", "\n", "# draw 1 sigma error band\n", "plt.fill_between(cx, y - yerr_boot, y + yerr_boot, facecolor=\"C1\", alpha=0.5)\n", "\n", "plt.legend(frameon=False,\n", " title=f\"$n = {m.values[0]:.2f} +/- {m.errors[0]:.2f}$\\n\"\n", " f\"$\\mu = {m.values[1]:.2f} +/- {m.errors[1]:.2f}$\\n\"\n", " f\"$\\sigma = {m.values[2]:.2f} +/- {m.errors[2]:.2f}$\");" ] }, { "cell_type": "markdown", "id": "greater-surge", "metadata": {}, "source": [ "The result is visually indistinguishable from before, as it should be. If you worry about deviations between the two methods, read on." ] }, { "cell_type": "markdown", "id": "excess-parking", "metadata": {}, "source": [ "In this example, computing the band from 1000 samples is slower than error propagation." ] }, { "cell_type": "code", "execution_count": 5, "id": "grave-month", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.72 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 100 loops each)\n" ] } ], "source": [ "%%timeit -r 1 -n 100\n", "par_b = rng.multivariate_normal(m.values, m.covariance, size=1000)\n", "y_b = [model(cx, p)[1] for p in par_b]\n", "np.std(y_b, axis=0)" ] }, { "cell_type": "markdown", "id": "cordless-elder", "metadata": {}, "source": [ "However, the calculation time scales linearly with the number of samples. One can simply draw fewer samples if the additional uncertainty is acceptable. If we draw only 50 samples, bootstrapping wins over numerical error propagation." ] }, { "cell_type": "code", "execution_count": 6, "id": "lucky-happening", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "258 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 1,000 loops each)\n" ] } ], "source": [ "%%timeit -r 1 -n 1000\n", "rng = np.random.default_rng(1)\n", "par_b = rng.multivariate_normal(m.values, m.covariance, size=50)\n", "y_b = [model(cx, p)[1] for p in par_b]\n", "np.std(y_b, axis=0)" ] }, { "cell_type": "markdown", "id": "moral-keeping", "metadata": {}, "source": [ "Let's see how the result looks, whether it deviates noticably." ] }, { "cell_type": "code", "execution_count": 7, "id": "brilliant-football", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABlRElEQVR4nO3deXhU5d038O85k30nZAeyENYAJmxBBBUlCq2iVqvVFx+FWnn0gbdFvGqhr0qxLahtlUfKg61PC1a0qK3IoqKIAi5AICFsYUvIAlkmgewJ2ebc7x8nmcyZmSSTZLYk3891zUXOOfe55zcnw8wv97kXSQghQERERORGZFcHQERERGSOCQoRERG5HSYoRERE5HaYoBAREZHbYYJCREREbocJChEREbkdJihERETkdpigEBERkdvxcHUAvaEoCoqLixEYGAhJklwdDhEREdlACIHa2lrExMRAlrtuI+mXCUpxcTFGjBjh6jCIiIioFy5fvozhw4d3WaZfJiiBgYEA1BcYFBTk4miIiIjIFjU1NRgxYoTxe7wr/TJBab+tExQUxASFiIion7GlewY7yRIREZHbYYJCREREbocJChEREbkdJihERETkdpigEBERkdthgkJERERuhwkKERERuR0mKEREROR2mKAQERGR22GCQkRERG6HCQoRERG5HSYoRERE5HaYoBARUZ8dPHgQCxYsQExMDCRJwscff2y13MaNGxEfHw8fHx/MmDED6enpvSrjrHp7a/HixXj++eftVp8t17e3r8/drl07JihE1C81NLcifuUniF/5CRqaW10dzqBXX1+P5ORkbNy4sdMy77//PlasWIHVq1cjMzMTycnJmDdvHsrKynpUxln1mpozZw62bNliU1mDwYDdu3fjnnvusam8Lbq7vr19fc64dr0m+qHq6moBQFRXV7s6FCJykfqmFhH3q90i7le7RX1Ti6vDcWs5OTkCgNi1a5e4/fbbha+vrxgzZow4fPiwQ54PgNi+fbvF/tTUVLF06VLjtsFgEDExMWLdunU9KuOsek3deuutYvPmzTaVPXjwoIiOjhaKothUvqesXd/evj5nXDtTPfn+ZgsKEdEAd+LECUiShNdeew0vvPACTpw4gdjYWKxcudKi7Nq1axEQENDlo7CwsMcxNDc3IyMjA2lpacZ9siwjLS0Nhw4dsrmMs+rti507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zouMjDQeM7du3TqsWbPG3qESUT/jytEc5B5Mk5HUhFAmJwOY3ROUDz74AO+++y7ee+89TJgwAVlZWVi+fDliYmLw+OOP96rOVatWae7NtbegENHg4s6jOcg5/Lw8kP/yXa4Og5zA7gnKL3/5S6xcuRIPP/wwAGDSpEkoKCjAunXr8PjjjyMqKgoAoNfrER3dcc9Qr9drbvmY8vb2hre3t9VjRDR4tI/mKK1utNoPRYLaJ8ERozmIyLns3km2oaHB2Lu5nU6nM07dm5CQgKioKOzbt894vKamBkeOHMHMmTPtHQ4RDSCuHM1BRM5l9wRlwYIF+P3vf49PPvkE+fn52L59O1577TX86Ec/AgBIkoTly5fjd7/7HXbu3IlTp07hscceQ0xMDO677z57h0NEA4wrR3MQkfPY/RbPhg0b8MILL+C//uu/UFZWhpiYGPznf/4nXnzxRWOZ5557DvX19ViyZAmqqqowe/Zs7NmzBz4+vG9MRN2bPzEas0aFcTQH0QBm9wQlMDAQ69evx/r16zstI0kSXnrpJbz00kv2fnoiGiQ4moNoYONaPEREROR2mKAQERGR22GCQkRERG6HCQoRERG5HSYoRERE5HaYoBAREZHbYYJCREREbocJChEREbkdJihERETkdpigEBERkdthgkJERERuhwkKERERuR0mKEREROR2mKAQERGR22GCQkRERG6HCQoRERG5HSYoRERE5HaYoBCRQzU0tyJ+5SeIX/kJGppbe13GXs9FRP0DExQiIiJyO0xQiMjtGBRh/Dk9r0KzTUSDAxMUInIre7P1SHvtgHF70eajmP3KV9hzusSFURGRszFBISK3snxbFvQ1TZp9pdWNeHprJpMUokGECQoRuRVrN3Pa963Zlc3bPUSDBBMUIuoXBICS6kak51W4OhQicgImKETUr5TVNro6BCJyAiYoRNSvRAT6uDoEInICJihE5FakLvZHB/sgNSHUmeEQkYswQSEit2OepLRvr16QBJ3cWQpDRAMJExQicivrH05BRJC3Zl9UsA82PToF8ydGuygqInI2D1cHQERk6o6kSNw+LgKTfvMFAGDL4um4eXQ4W06IBhm2oBCR2zFNRlITQpmcEA1CTFCIiIjI7TBBISIiIrfDBIWIiIjcDhMUIiIicjtMUIiIiMjtMEEhIiIit8MEhYiIiNwOExQiIiJy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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# compute bootstrapped curves with 50 samples\n", "par_b = rng.multivariate_normal(m.values, m.covariance, size=50)\n", "y_b = [model(cx, p)[1] for p in par_b]\n", "yerr_boot_50 = np.std(y_b, axis=0)\n", "\n", "# plot everything\n", "plt.errorbar(cx, d, derr, fmt=\"o\", label=\"data\", zorder=0)\n", "\n", "plt.plot(cx, y, lw=3, label=\"fit\")\n", "\n", "# draw 1 sigma error band\n", "plt.fill_between(cx, y - yerr_boot_50, y + yerr_boot_50, facecolor=\"C1\", alpha=0.5)\n", "\n", "plt.legend(frameon=False,\n", " title=f\"$n = {m.values[0]:.2f} +/- {m.errors[0]:.2f}$\\n\"\n", " f\"$\\mu = {m.values[1]:.2f} +/- {m.errors[1]:.2f}$\\n\"\n", " f\"$\\sigma = {m.values[2]:.2f} +/- {m.errors[2]:.2f}$\");" ] }, { "cell_type": "markdown", "id": "adequate-automation", "metadata": {}, "source": [ "No, the result is still visually indistinguishable. This suggests that 50 samples can be enough for plotting.\n", "\n", "Numerically, the three error bands differ at the 10 % level in the central region (expected relative error is $50^{-1/2} \\approx 0.14$). The eye cannot pickup these differences, but they are there. The curves differ more in the tails, which is not visible in linear scale, but noticable in log-scale." ] }, { "cell_type": "code", "execution_count": 8, "id": "hindu-moderator", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(12, 5))\n", "\n", "plt.sca(ax[0])\n", "plt.plot(cx, y - yerr_prop, \"-C0\", label=\"prop\")\n", "plt.plot(cx, y + yerr_prop, \"-C0\", label=\"prop\")\n", "plt.plot(cx, y - yerr_boot, \"--C1\", label=\"boot[1000]\")\n", "plt.plot(cx, y + yerr_boot, \"--C1\", label=\"boot[1000]\")\n", "plt.plot(cx, y - yerr_boot_50, \":C2\", label=\"boot[50]\")\n", "plt.plot(cx, y + yerr_boot_50, \":C2\", label=\"boot[50]\")\n", "plt.legend()\n", "plt.semilogy();\n", "\n", "plt.sca(ax[1])\n", "plt.plot(cx, yerr_boot / yerr_prop, label=\"boot[1000] / prop\")\n", "plt.plot(cx, yerr_boot_50 / yerr_prop, label=\"boot[50] / prop\")\n", "plt.legend()\n", "plt.axhline(1, ls=\"--\", color=\"0.5\", zorder=0)\n", "for delta in (-0.1, 0.1):\n", " plt.axhline(1 + delta, ls=\":\", color=\"0.5\", zorder=0)\n", "plt.ylim(0.5, 1.5);" ] }, { "cell_type": "markdown", "id": "quiet-watch", "metadata": {}, "source": [ "We see that the bootstrapped bands are a bit wider in the tails. This is caused by non-linearities that are neglected in error propagation." ] }, { "cell_type": "markdown", "id": "following-campaign", "metadata": {}, "source": [ "## Which is better? Error propagation or bootstrap?\n", "\n", "There is no clear-cut answer. At the visual level, both methods are usually fine (even with a small number of bootstrap samples). Which calculation is more accurate depends on details of the problem. Fortunately, the sources of error are orthogonal for both methods, so each method can be used to check the other.\n", "\n", "* The bootstrap error is caused by sampling. It can be reduced by drawing more samples, the relative error is proportional to $N^{-1/2}$.\n", "* The propagation error is caused by using a truncated Taylor series in the computation." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 5 } iminuit-2.24.0/doc/notebooks/external_minimizer.ipynb0000644000000000000000000000713714332717401017735 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using external minimizers\n", "\n", "We show how to use an external minimizer to find the minimum of a function and then use iminuit to compute the parameter uncertainties.\n", "\n", "We will demonstrate this with a maximum-likelihood fit of a normal distribution, which is carried out with `scipy.optimize.minimize`. iminuit is then used to compute the parameter uncertainties.\n", "\n", "Note: iminuit can call the scipy minimizers directly with `Minuit.scipy`, so `scipy.optimize.minimize` is only used here to demonstrate the general approach." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "import numpy as np\n", "from scipy.stats import norm\n", "from scipy.optimize import minimize" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# normally distributed data\n", "rng = np.random.default_rng(1)\n", "x = rng.normal(size=1000)\n", "\n", "# negative log-likelihood for a normal distribution\n", "def nll(par):\n", " return -np.sum(norm.logpdf(x, par[0], par[1]))\n", "\n", "nll.errordef = Minuit.LIKELIHOOD\n", "\n", "# minimize nll with scipy.optimize.minimize\n", "result = minimize(nll, np.ones(2))\n", "result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# initialize Minuit with the fit result from scipy.optimize.minimize\n", "m = Minuit(nll, result.x)\n", "m.hesse() # this also works without calling MIGRAD before" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also compute the \"Hesse errors\" at any other point than the minimum. These cannot be interpreted as parameter uncertainties, they are just some numbers related to the second derivative of the cost function at that point." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.values = (1.0, 0.5)\n", "m.hesse()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Minuit now reports that the minimum is invalid, which is correct, but it does not matter for the Hesse errors, which are computed anyway.\n", "\n", "Likewise, it one can also run MINOS to get MINOS estimates. Note that MINOS can fail if the starting point is not actually a minimum. So here we reset the values to the solution found by scipy.optimize." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m.values = result.x\n", "m.minos()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that MINOS ran successfully. The Hesse Errors were also updated, because MINOS needs HESSE to run first. HESSE is called automatically in this case." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/generic_least_squares.ipynb0000644000000000000000000001666614332717401020406 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Generic least-squares function\n", "\n", "This tutorial shows how to write a basic generic least-squares cost function that works well with iminuit.\n", "\n", "We have seen in the basic tutorial how to make a least-squares function with an explicit signature that iminuit could read to find the parameter names automatically. Part of the structure of a least-squares function is always the same. What changes is the model that predicts the y-values and its parameters. Here, we show how to make a generic [weighted least-squares](https://en.wikipedia.org/wiki/Weighted_least_squares) that extracts the parameters from the model which the user provides.\n", "\n", "Note: **Cost functions for common use-cases can be imported from `iminuit.cost`**, including a better version of a generic least-squares function that we build here. The built-in cost functions come with extra features and use some insights to make them work better than naive implementations, so prefer the built-in cost functions if you can." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Derive parameters from model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from iminuit import Minuit\n", "from iminuit.util import describe\n", "from typing import Annotated" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class LeastSquares:\n", " \"\"\"\n", " Generic least-squares cost function with error.\n", " \"\"\"\n", "\n", " errordef = Minuit.LEAST_SQUARES # for Minuit to compute errors correctly\n", " \n", " def __init__(self, model, x, y, err):\n", " self.model = model # model predicts y for given x\n", " self.x = np.asarray(x)\n", " self.y = np.asarray(y)\n", " self.err = np.asarray(err)\n", "\n", " def __call__(self, *par): # we must accept a variable number of model parameters\n", " ym = self.model(self.x, *par)\n", " return np.sum((self.y - ym) ** 2 / self.err ** 2)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's try it out with iminuit. We use a straight-line model which is only allowed to have positive slopes, and use an annotation with a slice to declare that, see `iminuit.util.describe` for details." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "def line(x, a: float, b: Annotated[float, 0:]):\n", " return a + b * x\n", "\n", "rng = np.random.default_rng(1)\n", "x_data = np.arange(1, 6, dtype=float)\n", "y_err = np.ones_like(x_data)\n", "y_data = line(x_data, 1, 2) + rng.normal(0, y_err)\n", "\n", "lsq = LeastSquares(line, x_data, y_data, y_err)\n", "\n", "# this fails\n", "try:\n", " m = Minuit(lsq, a=0, b=0)\n", " m.errordef=Minuit.LEAST_SQUARES\n", "except:\n", " import traceback\n", " traceback.print_exc()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happened? iminuit uses introspection to detect the parameter names and the number of parameters. It uses the `describe` utility for that, but it fails, since the generic method signature `LeastSquares.__call__(self, *par)`, does not reveal the number and names of the parameters.\n", "\n", "The information could be extracted from the model signature, but iminuit knows nothing about the signature of `line(x, a, b)` here. We can fix this by generating a function signature for the `LeastSquares` class from the signature of the model." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get the args from line\n", "describe(line, annotations=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# we inject that into the lsq object with the special attribute\n", "# `_parameters` that iminuit recognizes\n", "pars = describe(line, annotations=True)\n", "model_args = iter(pars)\n", "next(model_args) # we skip the first argument which is not a model parameter\n", "lsq._parameters = {k: pars[k] for k in model_args}\n", "\n", "# now we get the right answer\n", "describe(lsq, annotations=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can put this code into the init function of our generic least-squares class to obtain a generic least-squares class which works with iminuit." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class BetterLeastSquares(LeastSquares):\n", " def __init__(self, model, x, y, err):\n", " super().__init__(model, x, y, err)\n", " pars = describe(model, annotations=True)\n", " model_args = iter(pars)\n", " next(model_args)\n", " self._parameters = {k: pars[k] for k in model_args}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "lsq = BetterLeastSquares(line, x_data, y_data, y_err)\n", "\n", "m = Minuit(lsq, a=0, b=1)\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Report number of data points\n", "\n", "The minimum value of our least-squares function is asymptotically chi2-distributed. This is useful, because it allows us to judge the quality of the fit. iminuit automatically reports the reduced chi2 value $\\chi^2/n_\\text{dof}$ if the cost function has `errordef` equal to `Minuit.LEAST_SQUARES` and reports the number of data points." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class EvenBetterLeastSquares(BetterLeastSquares):\n", " @property\n", " def ndata(self):\n", " return len(self.x)\n", "\n", "lsq = EvenBetterLeastSquares(line, x_data, y_data, y_err)\n", "\n", "m = Minuit(lsq, a=0, b=0)\n", "m.migrad()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a developer of a cost function, you have to make sure that its minimum value is chi2-distributed if you add the property `ndata` to your cost function, `Minuit` has no way of knowing that.\n", "\n", "For binned likelihoods, one can make the minimum value asymptotically chi2-distributed by applying transformations, see [Baker and Cousins, Nucl.Instrum.Meth. 221 (1984) 437-442](https://doi.org/10.1016/0167-5087(84)90016-4).\n" ] } ], "metadata": { "kernelspec": { "display_name": "py310", "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.10.9" }, "vscode": { "interpreter": { "hash": "27d7f7c882b6255fd802da382767366ff09de66c661617e6bcea60578475bec2" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/gof.ipynb0000644000000000000000000022363314332717401014604 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# GoF from binned cost functions\n", "\n", "The builtin cost functions for binned data, `BinnedNLL` and `ExtendedBinnedNLL` have a minimum value which is asymptotically chi2-distributed and thus can be used as a goodness-of-fit statistic. This example shows, that one still needs a large number of entries in each bin to reach the asymptotic regime." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import BinnedNLL, ExtendedBinnedNLL, LeastSquares\n", "import numpy as np\n", "from numba_stats import norm, expon\n", "import matplotlib.pyplot as plt\n", "import joblib\n", "from scipy.stats import chi2" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "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MWOGnnrqKb838OY8J1pToDVu375do0aN0gcffKCysjI98MADGjt2rHbv3u3Xb+DAgX5z+Mknn7TG8K8o0PouqKio8Bt/TEyMvc2kOQy0vtdee82vrmPHjik6Ovqi16Ap81dSUqKcnBzt3LlTmzdv1rlz55SWlqba2tpL7mPMe6EVxO68804rJyfHXm9oaLDi4+OtgoKCJvv//Oc/tzIzM/3aUlJSrH//93+3LMuyGhsbLbfbbf3ud7+zt586dcpyOBzWqlWrWqGCKwu0xh87f/68FRkZaa1YscJuy87OtsaNG9fSQ22WQOtbvny55XK5Lnk80+bwWudv0aJFVmRkpHX69Gm7zaT5+yFJ1tq1ay/b5/nnn7cGDhzo1/bII49Y6enp9vq1/jtrTVdTY1MSExOt/Px8e33evHnW4MGDW25gLeRq6vv4448tSdb//d//XbKPqXPYnPlbu3atFRISYn3zzTd2m6nzZ1mWVVVVZUmySkpKLtnHlPfCoD2DUl9fr7KyMqWmptptoaGhSk1NVWlpaZP7lJaW+vWXpPT0dLv/kSNH5PV6/fq4XC6lpKRc8pitqTk1/tiZM2d07tw5RUdH+7UXFxcrJiZG/fr107Rp0/T999+36NivRnPrO336tHr37q2EhASNGzdO+/fvt7eZNIctMX/Lli3ThAkT1KVLF792E+avOa70GmyJf2emaWxsVE1NzUWvwYMHDyo+Pl433XSTJk6cqKNHj7bRCJtnyJAhiouL06hRo/Tpp5/a7cE2h8uWLVNqaqp69+7t127q/FVXV0vSRc+3HzLlvTBoA8rf//53NTQ0XHRn2tjY2Is+C73A6/Vetv+FfwZyzNbUnBp/bPbs2YqPj/d7oo0ePVp/+tOftHXrVv32t79VSUmJMjIy1NDQ0KLjv5Lm1NevXz+99dZbWrdunf785z+rsbFRd911l7799ltJZs3htc7fZ599pn379umpp57yazdl/prjUq9Bn8+nf/zjHy3ynDfNwoULdfr0af385z+321JSUlRYWKiNGzdqyZIlOnLkiO69917V1NS04UivTlxcnJYuXaq//vWv+utf/6qEhASNGDFCX375paSW+e+WKY4fP64PP/zwotegqfPX2NioGTNm6O6779agQYMu2c+U98I2u9U92t78+fO1evVqFRcX+11IOmHCBPvvpKQkJScnq2/fviouLtbIkSPbYqhXzePx+P3g5F133aUBAwbo97//vV5++eU2HFnLW7ZsmZKSknTnnXf6tbfn+fupKSoqUn5+vtatW+d3jUZGRob9d3JyslJSUtS7d2+98847mjx5clsM9ar169dP/fr1s9fvuusuHT58WIsWLdJ//dd/teHIWt6KFSsUFRWl8ePH+7WbOn85OTnat29fm10PE6igPYPSvXt3hYWFqbKy0q+9srJSbre7yX3cbvdl+1/4ZyDHbE3NqfGChQsXav78+froo4+UnJx82b433XSTunfvrkOHDl3zmANxLfVd0KFDB91222322E2aw2upr7a2VqtXr76q/9i11fw1x6Veg06nU506dWqR54QpVq9eraeeekrvvPPORafTfywqKkq33npru5jDptx555322INlDi3L0ltvvaVJkyYpIiLisn1NmL/p06drw4YN+vjjj9WzZ8/L9jXlvTBoA0pERISGDh2qrVu32m2NjY3aunWr3/9h/5DH4/HrL0mbN2+2+/fp00dut9uvj8/n065duy55zNbUnBqlf159/fLLL2vjxo0aNmzYFR/n22+/1ffff6+4uLgWGffVam59P9TQ0KDy8nJ77CbN4bXUt2bNGtXV1emxxx674uO01fw1x5Vegy3xnDDBqlWr9OSTT2rVqlV+XxG/lNOnT+vw4cPtYg6bsmfPHnvswTKHJSUlOnTo0FX9T0Jbzp9lWZo+fbrWrl2rbdu2qU+fPlfcx5j3wha73NZAq1evthwOh1VYWGgdOHDAmjp1qhUVFWV5vV7Lsixr0qRJ1gsvvGD3//TTT63w8HBr4cKF1ldffWXNmzfP6tChg1VeXm73mT9/vhUVFWWtW7fO2rt3rzVu3DirT58+1j/+8Y/rXp9lBV7j/PnzrYiICOsvf/mLdeLECXupqamxLMuyampqrF/+8pdWaWmpdeTIEWvLli3W7bffbt1yyy3W2bNnja8vPz/f2rRpk3X48GGrrKzMmjBhgtWxY0dr//79dh+T5jDQ+i645557rEceeeSidtPmr6amxtq9e7e1e/duS5L1yiuvWLt377b+9re/WZZlWS+88II1adIku////M//WJ07d7ZmzZplffXVV9bixYutsLAwa+PGjXafK/07u94CrXHlypVWeHi4tXjxYr/X4KlTp+w+zz33nFVcXGwdOXLE+vTTT63U1FSre/fuVlVVlfH1LVq0yHrvvfesgwcPWuXl5dazzz5rhYaGWlu2bLH7mDSHgdZ3wWOPPWalpKQ0eUyT5m/atGmWy+WyiouL/Z5vZ86csfuY+l4Y1AHFsizrjTfesHr16mVFRERYd955p7Vz50572/33329lZ2f79X/nnXesW2+91YqIiLAGDhxovf/++37bGxsbrV//+tdWbGys5XA4rJEjR1oVFRXXo5RLCqTG3r17W5IuWubNm2dZlmWdOXPGSktLs3r06GF16NDB6t27tzVlypQ2+4+/ZQVW34wZM+y+sbGx1pgxY6wvv/zS73imzWGgz9Gvv/7akmR99NFHFx3LtPm78JXTHy8XasrOzrbuv//+i/YZMmSIFRERYd10003W8uXLLzru5f6dXW+B1nj//fdftr9l/fOr1XFxcVZERIT1L//yL9YjjzxiHTp06PoW9v8FWt9vf/tbq2/fvlbHjh2t6Ohoa8SIEda2bdsuOq4pc9ic5+ipU6esTp06WX/4wx+aPKZJ89dUbZL8XlemvheG/P8CAAAAjBG016AAAID2i4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOP8PyTj8qOs+KBdAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def generate(n, seed):\n", " rng = np.random.default_rng(seed)\n", " s = rng.normal(1, 0.1, size=rng.poisson(n))\n", " b = rng.exponential(size=rng.poisson(n))\n", " x = np.append(s, b)\n", " return x[(x > 0) & (x < 2)]\n", "\n", "x = generate(1000, 1)\n", "plt.hist(x, bins=20, range=(0, 2));" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "@joblib.delayed\n", "def run(n, seed):\n", " x = generate(n, seed)\n", " xrange = (0, 2)\n", " w, xe = np.histogram(x, bins=20, range=xrange)\n", "\n", " def model1(x, z, mu, sigma, tau):\n", " return z * norm.cdf(x, mu, sigma) / np.diff(norm.cdf(xrange, mu, sigma)) + (1 - z) * expon.cdf(x, 0, tau) / np.diff(expon.cdf(xrange, 0, tau))\n", "\n", " def model2(x, s, b, mu, sigma, tau):\n", " return s * n * norm.cdf(x, mu, sigma) + b * n * expon.cdf(x, 0, tau)\n", "\n", " m = [\n", " Minuit(BinnedNLL(w, xe, model1), z=0.5, mu=0.5, sigma=0.5, tau=0.5),\n", " Minuit(ExtendedBinnedNLL(w, xe, model2), s=1, b=1, mu=0.5, sigma=0.5, tau=0.5),\n", " ]\n", " for mi in m:\n", " mi.limits[\"mu\"] = (0, 2)\n", " mi.limits[\"sigma\", \"tau\"] = (0.1, None)\n", " m[0].limits[\"z\"] = (0, 1)\n", " m[1].limits[\"s\", \"b\"] = (0, None)\n", " r = []\n", " for mi in m:\n", " mi.migrad()\n", " if mi.valid:\n", " pvalue = 1 - chi2(mi.fcn._fcn.ndata - mi.nfit).cdf(mi.fval)\n", " r.append(pvalue)\n", " else:\n", " r.append(np.nan)\n", " return r\n", "\n", "pvalues = {}\n", "for n in (20, 100, 1000, 10000):\n", " pvalues[n] = np.array(joblib.Parallel(-1)(run(n, i) for i in range(500)))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/png": "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ixYt0ty5c/Xpp5/qT3/6kySpW7dukqTZs2erffv2+uUvfyk/Pz999NFHeuKJJ1RUVKTk5GSnrvPll1+qe/fuatKkiZ5//nnVqVNH77//vu699179/e9/13333SdJysnJ0Z133qkLFy7Y682dO1fBwcFXvcYLL7yg1q1ba+7cuZoyZYqio6N14403lqjXsGFDzZ49W6NHj9Z9992nQYMGSZI6duwoSUpMTNSXX36pMWPGqHnz5jp27JjWrVungwcPlvtQFJvNppMnT1bo53H577U0O3bsUKtWrRQSEuJQftttt0mSdu7cqcjIyApdC/B21TWPl8aZPO7qPDBo0CCFhoZq3LhxeuCBB9SvXz/VrVtXkrRt2zZt2bJFQ4cOVdOmTXXgwAHNnj1bvXr10u7du3Xdddc5dZ+PP/64FixYoBEjRuipp57S/v379cc//lE7duzQ5s2b5e/vL0l66aWXNHXqVPXr10/9+vXT559/rj59+ujcuXPltt+2bVstWrRI48aNU9OmTfXMM89IupTfjx8/XiKWI0eOaN26dVq0aJHDuXXr1umBBx7QXXfdZf972bNnjzZv3qynn3663BhOnDihixcvXvVncd1115X786uKz3agJvH1HE7/vnz074HqjzxO/57+vSP69y7m6W8HUHWKv2F85JFHHMrvu+8+06BBgwq//2pHZZcCvfzyy0aSyczMtJcVfwP88ccfl6g/ePBgEx4eXqlrJSUllbrs9cyZMyXKEhISTIsWLRzKKjKz5q677jIdOnRw2BeyqKjIdOvWzbRs2dJeNnbsWCPJbN261V527NgxY7VaK7TstXh57JXLXq/85resZa8nTpyo9JKw4mtX5Lia9u3bm969e5co//LLL40kM2fOHKfjA7xNdc3j5c3WcSaPV0UeKM7PV+a40vJ9VlaWkWTeffdde1lFZtb8+9//NpLMe++959DemjVrHMqPHTtmAgICTP/+/U1RUZG93u9+9zuHmZHliYqKKrHstbTPoLKWvT799NMmJCTEYf/KioqKiqrQ38/Vtleoqs92oLojh89xuk1n0L//Gf17oGqQx53P4/Tvk656P/TvcSVmzvuA3/zmNw6vb7/9di1btkwFBQUlvs283PDhw9WjR4+rtl+RGSFX+vjjjzV58mQNGTJEvXv3tpefPXtWkhQYGFjiPUFBQfbzrnJ57Pn5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/PnzXXINwNe5ep/LqVOnSpK+/PJLSZc6X5988okk6cUXX7TXq2gedyYPTJo0SZMnT9aGDRvUq1cvp2MfMGCAFi1aJKvVqnbt2ikrK0vr16936NhWVM+ePfX4448rLS1NO3fuVJ8+feTv7699+/Zp6dKl+sMf/qD7779fDRs21LPPPqu0tDQNGDBA/fr1044dO7R69WrdcMMNTl+3PDExMZKkp556SgkJCapdu7aGDh2qRx99VD/99JN69+6tpk2b6vvvv9fbb7+tTp06qW3btuW26ao9KSXnPtsBXOJNOZz+vWvQvwdqFl/M4/TvXYf+vQ8x8FoTJ040kszx48cdyjMyMowks3//frfFEhUVZSSVekRFRZWo/+9//9t069bNBAUFmYYNG5rk5GRTUFDgUOfkyZNGkhk6dOhVr5+UlGTq1KlTonzFihWmY8eOJigoyDRv3ty8+uqr5s9//nOJn0/Pnj1Nz5497a/3799vJJmMjAyH9r799lszfPhwEx4ebvz9/U2TJk3MgAEDzAcffOBQ77///a/p2bOnCQoKMk2aNDEvv/yymT9/foV+L8W/v23btpW4xyt/llu2bDExMTEmICDASDITJ040//vf/0xycrJp06aNqVOnjrFarSY2Nta8//775V63Kpw9e9Y8++yzJjw83AQGBppbb73VrFmzxu1xANVVdcrjxpgy83hp3YmK5HFjKp4HnnnmGWOxWMyePXvKjbE4P7/++usO5SdOnDAjRowwN9xwg6lbt65JSEgwX331lYmKijJJSUn2ehs2bDCSzIYNG+xlpeVXY4yZO3euiYmJMcHBwaZevXqmQ4cOZvz48ebIkSP2OhcvXjSTJ082ERERJjg42PTq1ct88cUXJa5blqioKNO/f/9S7/Hyz6ALFy6YMWPGmIYNGxqLxWL/nXzwwQemT58+plGjRiYgIMA0a9bMPP744+bo0aNXvbarVfRvAvAW5HDn26R/T/8eqE7I41XbJv37kvdI/943WYy5ysZxQDW1atUqDRgwQP/5z3/UoUMHT4cDAKhCt912m6KiorR06VJPhwIAqCL07wHAd9C/By5hWxvUWBs2bNDQoUPpuAOAlysoKNB//vMfLVy40NOhAACqEP17APAN9O+BnzFzHgAAAAAAAAAAN6vl6QAAAAAAAAAAAPA1DM4DAAAAAAAAAOBmDM4DAAAA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9etrz5499rKCggKtW7dODz74oEJCQuzlw4cPV926dfX+++87fW2LxaKMjAydPn1aFovFYfuUjIwM9e7dW40aNVJgYKDatWun2bNnl2jjyj0py/LVV1/p/vvvV/369RUUFKQuXbpoxYoVJep9+eWX6t27t4KDg9W0aVNNnTpVRUVFV22/V69eSkpKkiTdeuutslgs9j3NLt8z7cCBA2rYsKEkafLkyfb7Ll4SlZOToxEjRqhp06YKDAxURESE7rnnngrth+kqgYGBCg8Pd9v1gJqmOuXwevXqqX79+let50wO/+CDD1S7dm2NGjXKXhYUFKSRI0cqKytLhw4dcirGSZMmKSoqSpL029/+VhaLxZ4Tv//+ez3xxBNq3bq1goOD1aBBAw0ePLhEzittP8rSFBUVaebMmWrfvr2CgoIUFhamxx9/XCdOnHCoZ4zR1KlT1bRpU1133XW688479eWXX171Xorj2L9/v1auXGnP4QcOHCixH+XDDz+s9PR0SbLXs1gs9raWLFmimJgY1atXTyEhIerQoYP+8Ic/XDUGV7nhhhvUvn37EuX33XefJDn0AQBvQx4/5HSbFUX/vnr277t166aAgACHspYtW6p9+/bke9RI5HHn8zh9/LLRx0dZ/DwdAKrekCFDFB0drbS0NH3++ef605/+pEaNGunVV18t931nzpyp0AdO7dq1df311zsd16lTp3Tq1CndcMMN9rJdu3bpwoULJfYPCwgIUKdOnbRjxw6nr7No0SLNnTtXn376qf70pz9JutRxlKTZs2erffv2+uUvfyk/Pz999NFHeuKJJ1RUVKTk5GSnrvPll1+qe/fuatKkiZ5//nnVqVNH77//vu699179/e9/tyepnJwc3Xnnnbpw4YK93ty5cxUcHHzVa7zwwgtq3bq15s6dqylTpig6Olo33nhjiXoNGzbU7NmzNXr0aN13330aNGiQJKljx46SpMTERH355ZcaM2aMmjdvrmPHjmndunU6ePBguQ9FsdlsOnnyZIV+Hpf/XgFUXnXN4aVxJofv2LFDrVq1cujgS9Jtt90mSdq5c6ciIyMrfO1BgwYpNDRU48aN0wMPPKB+/fqpbt26ki7tgbtlyxYNHTpUTZs21YEDBzR79mz16tVLu3fv1nXXXefUfT7++ONasGCBRowYoaeeekr79+/XH//4R+3YsUObN2+W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nzF1/PhxnThxwunrlqe8fB8QEKCBAwdq4MCBKioq0hNPPKF33nlHEyZMKHdQ6dZbb7XPDCrPxIkTNWnSpKvW+8c//qFHH31UgwYNUnp6+lXrA/CePE7/3nVqQv++sLBQAwcO1Ndff63169erXbt2FWob8Ea+mMfp47sOfXzfweA8yuTK/dFOnz6tfv366fDhw9qwYUOJJUDFrFar4uPj9Ze//EUTJkywz6xftGiRTp06pcGDBzt3E+Uo/vb7yqVPGRkZTrfVqFEj9erVS++8847GjBlT4ouH48eP279p7devn2bOnKlPP/3Uvi/l8ePHy/yGtrKKn6x95QfTmTNnVKtWLfueb9KlD6J69eqVuvzscuw5D9Qcntjj0pkcfv/99+v3v/+95s6dq2effVbSpaWyGRkZio2NVWRkpMviql27dokBhbfffrtCs0auNGTIEM2aNUsvv/xyiX0lL1y4oFOnTik0NFTx8fHy9/fX22+/rT59+tg71zNnzqz0fZSlTp06ki7l+9DQUHv5jz/+qAYNGthf16pVSx07dpRU+rLky7lyP8qPP/5YQ4cO1R133KH33ntPtWrVuup7AHhPHqd/7zrVvX9/8eJF/epXv1JWVpb+8Y9/KC4uziXtAjWVL+Zx+viuQx/fdzA4jzK5cn+0YcOG6dNPP9UjjzyiPXv2aM+ePfZzdevW1b333mt//corr6hbt27q2bOnRo0apR9++EFvvPGG+vTpU2IPLYvFop49e2rjxo1Ox9SnTx/7t42PP/64Tp06pXnz5qlRo0alLte6mvT0dPXo0UMdOnTQY489phYtWig3N1dZWVn64Ycf9J///EeSNH78eC1atEi/+MUv9PTTT6tOnTqaO3euoqKi9N///tfp65YlODhY7dq109/+9je1atVK9evX180336wLFy7orrvu0pAhQ9SuXTv5+flp2bJlys3N1dChQ8tt09V7Uv7xj39UXl6ejhw5IunSbKcffvhB0qUlahXdExRASa7e43Lq1KmSpC+//FLSpc74J598IkkOyxcrmsNjY2M1ePBgpaam6tixY7rpppu0cOFCHThwQPPnz3e49qRJkzR58mRt2LBBvXr1cjr2AQMGaNGiRbJarWrXrp2ysrK0fv16h05tRfXs2VOPP/640tLStHPnTvXp00f+/v7at2+fli5dqj/84Q+6//771bBh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xiorK6vUNm02mwoKChwOAAAAANdm3759aty4sVq0aKFhw4bp4MGDkqTt27fr/PnzDn32Nm3aqFmzZmX22SX67QAAAICrOTVz/rvvvtPs2bOVkpKi3/3ud9q2bZueeuopBQQEKCkpSTk5OZKksLAwh/eFhYXZz10pLS1NkydPrmT4pfPkUjYAqIkOHz6s5557TqtXr9aZM2d00003KSMjQ126dJF0aWXUxIkTNW/ePOXl5al79+6aPXu2WrZs6eHIAQCliY2N1YIFC9S6dWsdPXpUkydP1u23364vvvhCOTk5CggIUGhoqMN7yuuzS1XTbwcAAAB8mVMz54uKinTLLbdo2rRp6ty5s0aNGqXHHntMc+bMqXQAqampys/Ptx+HDh2qdFsAAOcVP0/E399fq1ev1u7du/XGG2+U+jyROXPmaOvWrapTp44SEhJUWFjowcgBAGXp27evBg8erI4dOyohIUGrVq1SXl6e3n///Uq3Sb8dAAAAcC2nZs5HRESoXbt2DmVt27bV3//+d0my72mZm5uriIgIe53c3Fx16tSp1DYDAwMVGBjoTBgAABfieSIA4P1CQ0PVqlUrffPNN7r77rt17tw55eXlOcyez83NLXWP+mL02wEAAADXcmpwvnv37tq7d69D2ddff62oqChJlwZzwsPDlZmZaR+MLygo0NatWzV69GjXRAwAcKkVK1YoISFBgwcP1qZNm9SkSRM98cQTeuyxxyRd/XkipQ3O22w22Ww2+2v2JQYAzzp16pS+/fZbPfTQQ4qJiZG/v78yMzOVmJgoSdq7d68OHjyouLg4D0cKAABQvXlyO+0D0/t77NqoGk5tazNu3DhlZ2dr2rRp+uabb7R48WLNnTtXycnJkiSLxaKxY8dq6tSpWrFihXbt2qXhw4ercePGuvfee6sifgDANSp+nkjLli21du1ajR49Wk899ZQWLlwoSZV+nojVarUfkZGRVXsTAAAHzz77rDZt2qQDBw5oy5Ytuu+++1S7dm098MADslqtGjlypFJSUrRhwwZt375dI0aMUFxcnLp27erp0AEAAACf4dTM+VtvvVXLli1TamqqpkyZoujoaM2cOVPDhg2z1xk/frxOnz6tUaNGKS8vTz169NCaNWsUFBTk8uABANeuqKhIXbp00bRp0yRJnTt31hdffKE5c+YoKSmpUm2mpqYqJSXF/rqgoIABegBwox9++EEPPPCAfvzxRzVs2FA9evRQdna2GjZsKEmaMWOGatWqpcTERNlsNiUkJGjWrFkejhoAAADwLU4NzkvSgAEDNGDAgDLPWywWTZkyRVOmTLmmwAAA7sHzRADA+yxZsqTc80FBQUpPT1d6erqbIgIAAABwJae2tQEAeB9nnidSrPh5IuxNDAAAAAAAUDlOz5wHAHiXcePGqVu3bpo2bZqGDBmiTz/9VHPnztXcuXMlOT5PpGXLloqOjtaECRN4nggAAAAAAMA1YHAeAHwczxMBAAAAAABwPwbnAQA8TwQAAAAAAMDN2HMeAAAAAAAAAAA3Y3AeAAAAAAAAAAA3Y3AeAAAAAAAAAAA3Y3AeAAAAAACghpk+fbosFovGjh1rLyssLFRycrIaNGigunXrKjExUbm5uZ4LEgBQLgbnAQAAAAAAapBt27bpnXfeUceOHR3Kx40bp48++khLly7Vpk2bdOTIEQ0aNMhDUQIArobBeQAAAAAAgBri1KlTGjZsmObNm6frr7/eXp6fn6/58+frzTffVO/evRUTE6OMjAxt2bJF2dnZHowYAFAWP08HAAAAAAAAXKv58ys9du0D0/t77Nq+IDk5Wf3791d8fLymTp1qL9++fbvOnz+v+Ph4e1mbNm3UrFkzZWVlqWvXriXastlsstls9tcFBQVVGzwAwAGD8wAAoARP/oPekxhMAAAA1dmSJUv0+eefa9u2bSXO5eTkKCAgQKGhoQ7lYWFhysnJKbW9tLQ0TZ48uSpCBQBUAIPzqNGYDQIAAAAA8AWHDh3S008/rXXr1ikoKMglbaampiolJcX+uqCgQJGRkS5pGwBwdew5DwAAAAAAUM1t375dx44d0y233CI/Pz/5+flp06ZNeuutt+Tn56ewsDCdO3dOeXl5Du/Lzc1VeHh4qW0GBgYqJCTE4QAAuA8z5wEAAAAAAKq5u+66S7t27XIoGzFihNq0aaPnnntOkZGR8vf3V2ZmphITEyVJe/fu1cGDBxUXF+eJkAEAV8HgPFBJbKkDAAAAAHCXevXq6eabb3Yoq1Onjho0aGAvHzlypFJSUlS/fn2FhIRozJgxiouLK/VhsABqHl99Npg3Y3AeAIBqio4XAAAAnDFjxgzVqlVLiYmJstlsSkhI0KxZszwdFgCgDAzOAwAAAAAA1EAbN250eB0UFKT09HSlp6d7JiAAgFN4ICwAAAAAAAAAAG7G4DwAAAAAAAAAAG7G4DwAAAAAAAAAAG7G4DwAAAAAAAAAAG7GA2EBAAAAAAAAOKX58ys9HQJQ4zE4DwAAAHi56dOnKzU1VU8//bRmzpwpSSosLNQzzzyjJUuWyGazKSEhQbNmzVJYWJhngwVQ4zFgBwBAxbCtDQAAAODFtm3bpnfeeUcdO3Z0KB83bpw++ugjLV26VJs2bdKRI0c0aNAgD0UJAAAA+B4G5wEAAAAvderUKQ0bNkzz5s3T9ddfby/Pz8/X/Pnz9eabb6p3796KiYlRRkaGtmzZouzsbA9GDAAAAPgOBucBAAAAL5WcnKz+/fsrPj7eoXz79u06f/68Q3mbNm3UrFkzZWVlldqWzWZTQUGBwwEAAACg8thzHgAA4P/nyT1yD0zv77FrwzstWbJEn3/+ubZt21biXE5OjgICAhQaGupQHhYWppycnFLbS0tL0+TJk6siVPgA9iAHAAAoiZnzAAAAgJc5dOiQnn76ab333nsKCgpySZupqanKz8+3H4cOHXJJuwAAAICvYnAeAAAA8DLbt2/XsWPHdMstt8jPz09+fn7atGmT3nrrLfn5+SksLEznzp1TXl6ew/tyc3MVHh5eapuBgYEKCQlxOAAAAABUHtvaAAAA+Di28/E+d911l3bt2uVQNmLECLVp00bPPfecIiMj5e/vr8zMTCUmJkqS9u7dq4MHDyouLs4TIQMAAAA+h8F5AADw/7V3/9FR1Xf+x18hPyZokomJmwQkWRCroIisUWFEKKapKVIqS3aNiweBg3XbTlghZ9uSWkWtEup6wB8dcOvGpD9MQ3GJlh+GZQMJqyZWozmLoqku0NDFCXK2STA2k5h8vn/wZWQkgJNk7iRzn49z7jnez3zm3vcH7Cv2PTefARBhEhMTNWXKlICxCy+8UKmpqf7x5cuXq6ioSCkpKUpKStKKFSvkcrk0Y8aMcJQMAAAA2A7NeQAAAMCGNmzYoFGjRik/P18+n095eXnauHFjuMsCAAAAbIPmPAAAAGADtbW1Aefx8fHyeDzyeDzhKQgAAACwOb4QFgAAAAAAAAAAi9GcBwAAAAAAAADAYjTnAQAAAAAAAACwGM15AAAAAAAAAAAsRnMeAAAAAAAAAACL0ZwHAAAAAAAAAMBiMeEuAEDwxq/eEbZ7H143L2z3BgAAAAAAACIFT84DAAAAAAAAAGAxmvMAAAAAAAAAAFiM5jwAAAAAAAAAABajOQ8AAAAAAAAAgMUG1Zxft26doqKitHLlSv9YV1eX3G63UlNTlZCQoPz8fLW2tg62TgAAAAAAANvatGmTpk6dqqSkJCUlJcnlcunll1/2v04/BgBGngE359944w3967/+q6ZOnRowvmrVKm3btk1btmxRXV2djh49qoULFw66UAAAAAAAALsaN26c1q1bp8bGRr355pvKycnRbbfdpnfffVcS/RgAGIliBvKmTz75RHfeeaeeffZZPfLII/7x9vZ2lZaWqqKiQjk5OZKksrIyTZ48WQ0NDZoxY8bQVA0AAAAAwDA3fvWOcJeACDJ//vyA80cffVSbNm1SQ0ODxo0bN6B+jM/nk8/n8593dHSEbgEAgDMM6Ml5t9utefPmKTc3N2C8sbFRPT09AeOTJk1SVlaW6uvr+72Wz+dTR0dHwAEACB+2LAMAAACGt97eXlVWVqqzs1Mul2tA/RhJKikpkdPp9B+ZmZlWlA8A+P+CfnK+srJSb731lt54440zXvN6vYqLi1NycnLAeHp6urxeb7/XKykp0UMPPRRsGQCAEDjXlmU7duzQli1b5HQ6VVhYqIULF+rVV18NU6VA5OHpSgAAcD779++Xy+VSV1eXEhISVFVVpSuvvFJNTU1B92Mkqbi4WEVFRf7zjo4OGvQAYKGgnpw/cuSI7r33Xj3//POKj48fkgKKi4vV3t7uP44cOTIk1wUABOf0Lcsuuugi//ipLcvWr1+vnJwcZWdnq6ysTK+99poaGhrCWDEAAABgL1dccYWampr0+uuv67vf/a6WLFmiAwcODPh6DofD/wWzpw4AgHWCas43Njbq2LFjuvbaaxUTE6OYmBjV1dXpqaeeUkxMjNLT09Xd3a22traA97W2tiojI6Pfa/KDAACGB7YsAwAAAIa3uLg4XXbZZcrOzlZJSYmuueYaPfnkk8rIyAi6HwMACL+gmvNf+9rXtH//fjU1NfmP6667Tnfeeaf/n2NjY1VTU+N/T3Nzs1paWuRyuYa8eADA0Di1ZVlJSckZrw10yzL2rgQAAABCq6+vTz6fT9nZ2fRjAGAECmrP+cTERE2ZMiVg7MILL1Rqaqp/fPny5SoqKlJKSoqSkpK0YsUKuVyus34zOAAgvE5tWbZ79+4h3bKMvSsBAACAoVNcXKy5c+cqKytLJ06cUEVFhWpra7Vr1y45nU76MQAwAgX9hbDns2HDBo0aNUr5+fny+XzKy8vTxo0bh/o2AMIknF9YeHjdvLDdO5KdvmXZKb29vdq3b59+9rOfadeuXf5fkT396fnzbVnmcDhCXToAAABgG8eOHdNdd92ljz76SE6nU1OnTtWuXbv09a9/XRL9GAAYiQbdnK+trQ04j4+Pl8fjkcfjGeylAQAWOLVl2emWLVumSZMm6Yc//KEyMzP9vyKbn58viV+RBQAAAKxWWlp6ztfpxwDAyBPUnvMAgMhzasuy04/Ttyw7/Vdk9+7dq8bGRi1btoxfkQWAYWzTpk2aOnWqkpKSlJSUJJfLpZdfftn/eldXl9xut1JTU5WQkKD8/Hy1traGsWIAAADAfmjOAwDOa8OGDfrmN7+p/Px8zZ49WxkZGdq6dWu4ywIAnMW4ceO0bt06NTY26s0331ROTo5uu+02vfvuu5KkVatWadu2bdqyZYvq6up09OhRLVy4MMxVAwAAAPYy5HvOAwBGPrYsA4CRbf78+QHnjz76qDZt2qSGhgaNGzdOpaWlqqioUE5OjiSprKxMkydPVkNDA78VBQAAAFiEJ+cBAACACNbb26vKykp1dnbK5XKpsbFRPT09ys3N9c+ZNGmSsrKyVF9ff9br+Hw+dXR0BBwAAAAABo7mPAAAABCB9u/fr4SEBDkcDn3nO99RVVWVrrzySnm9XsXFxSk5OTlgfnp6urxe71mvV1JSIqfT6T8yMzNDvAIAAAAgstGcBwAAACLQFVdcoaamJr3++uv67ne/qyVLlujAgQMDvl5xcbHa29v9x5EjR4awWgAAAMB+2HMeAAAAiEBxcXG67LLLJEnZ2dl644039OSTT6qgoEDd3d1qa2sLeHq+tbVVGRkZZ72ew+GQw+EIddkAAACAbfDkPAAAAGADfX198vl8ys7OVmxsrGpqavyvNTc3q6WlRS6XK4wVAgAAAPbCk/MAAABAhCkuLtbcuXOVlZWlEydOqKKiQrW1tdq1a5ecTqeWL1+uoqIipaSkKCkpSStWrJDL5dKMGTPCXToAAABgGzTnAQAAgAhz7Ngx3XXXXfroo4/kdDo1depU7dq1S1//+tclSRs2bNCoUaOUn58vn8+nvLw8bdy4McxVAwAAAPZCcx4AAACIMKWlped8PT4+Xh6PRx6Px6KKAAAAAHwRe84DAAAAAAAAAGAxmvMAAAAAAAAAAFiM5jwAAAAAAAAAABajOQ8AAAAAAAAAgMVozgMAAAAAAAAAYDGa8wAAAAAAAAAAWIzmPAAAAAAAAAAAFosJdwEA8GWNX70jLPc9vG5eWO4LAAAAAACAyMWT8wAAAAAAAAAAWIzmPAAAAAAAAAAAFqM5DwAAAAAAAACAxWjOAwAAAAAAAABgMZrzAAAAAAAAAABYjOY8AAAAAAAAAAAWozkPAAAAAAAAAIDFaM4DAAAAAAAMcyUlJbr++uuVmJiotLQ0LViwQM3NzQFzurq65Ha7lZqaqoSEBOXn56u1tTVMFQMAzofmPAAAAAAAwDBXV1cnt9uthoYG7d69Wz09PbrlllvU2dnpn7Nq1Spt27ZNW7ZsUV1dnY4ePaqFCxeGsWoAwLnEhLsAAAAAAAAAnFt1dXXAeXl5udLS0tTY2KjZs2ervb1dpaWlqqioUE5OjiSprKxMkydPVkNDg2bMmBGOsgEA58CT8wAAAAAAACNMe3u7JCklJUWS1NjYqJ6eHuXm5vrnTJo0SVlZWaqvr+/3Gj6fTx0dHQEHAMA6NOcBAAAAAABGkL6+Pq1cuVIzZ87UlClTJEler1dxcXFKTk4OmJueni6v19vvdUpKSuR0Ov1HZmZmqEsHAJyG5jwAAAAAAMAI4na79c4776iysnJQ1ykuLlZ7e7v/OHLkyBBVCAD4MthzHgAAAAAAYIQoLCzU9u3btW/fPo0bN84/npGRoe7ubrW1tQU8Pd/a2qqMjIx+r+VwOORwOEJdMgDgLHhyHgAAAAAAYJgzxqiwsFBVVVXas2ePJkyYEPB6dna2YmNjVVNT4x9rbm5WS0uLXC6X1eUCAL4EmvMAAABAhCkpKdH111+vxMREpaWlacGCBWpubg6Y09XVJbfbrdTUVCUkJCg/P1+tra1hqhgAcD5ut1u//vWvVVFRocTERHm9Xnm9Xv3lL3+RJDmdTi1fvlxFRUXau3evGhsbtWzZMrlcLs2YMSPM1QMA+kNzHgAAAIgwdXV1crvdamho0O7du9XT06NbbrlFnZ2d/jmrVq3Stm3btGXLFtXV1eno0aNauHBhGKsGAJzLpk2b1N7erjlz5mjMmDH+Y/Pmzf45GzZs0De/+U3l5+dr9uzZysjI0NatW8NYNQDgXNhzHgAAAIgw1dXVAefl5eVKS0tTY2OjZs+erfb2dpWWlqqiokI5OTmSpLKyMk2ePFkNDQ08YQkAw5Ax5rxz4uPj5fF45PF4LKgIADBYNOcBAAAQNuNX7wjLfQ+vmxeW+4ZLe3u7JCklJUWS1NjYqJ6eHuXm5vrnTJo0SVlZWaqvr++3Oe/z+eTz+fznHR0dIa4aAAAAiGxsawMAAABEsL6+Pq1cuVIzZ87UlClTJEler1dxcXFKTk4OmJueni6v19vvdUpKSuR0Ov1HZmZmqEsHAAAAIhrNeQAAACCCud1uvfPOO6qsrBzUdYqLi9Xe3u4/jhw5MkQVAgAAAPbEtjYAAABAhCosLNT27du1b98+jRs3zj+ekZGh7u5utbW1BTw939raqoyMjH6v5XA45HA4Ql0yAAAAYBs8OQ8AAABEGGOMCgsLVVVVpT179mjChAkBr2dnZys2NlY1NTX+sebmZrW0tMjlclldLgAAAGBLPDkPAAAARBi3262Kigq99NJLSkxM9O8j73Q6NXr0aDmdTi1fvlxFRUVKSUlRUlKSVqxYIZfL1e+XwQIAAAAYejTnAQAAgAizadMmSdKcOXMCxsvKyrR06VJJ0oYNGzRq1Cjl5+fL5/MpLy9PGzdutLhSAAAAwL5ozgMAAAARxhhz3jnx8fHyeDzyeDwWVAQAAADgi4Lac76kpETXX3+9EhMTlZaWpgULFqi5uTlgTldXl9xut1JTU5WQkKD8/Hy1trYOadEAAAAAAAAAAIxkQTXn6+rq5Ha71dDQoN27d6unp0e33HKLOjs7/XNWrVqlbdu2acuWLaqrq9PRo0e1cOHCIS8cADA0+OAVAAAAAADAekE156urq7V06VJdddVVuuaaa1ReXq6WlhY1NjZKktrb21VaWqr169crJydH2dnZKisr02uvvaaGhoaQLAAAMDh88AoAAAAAAGC9Qe05397eLklKSUmRJDU2Nqqnp0e5ubn+OZMmTVJWVpbq6+s1Y8aMM67h8/nk8/n85x0dHYMpCQAQpOrq6oDz8vJypaWlqbGxUbNnz/Z/8FpRUaGcnBxJJ79QcPLkyWpoaOg32wEAAAAAAHBuQT05f7q+vj6tXLlSM2fO1JQpUyRJXq9XcXFxSk5ODpibnp4ur9fb73VKSkrkdDr9R2Zm5kBLAgAMgWA/eO2Pz+dTR0dHwAEAAAAAAIDPDbg573a79c4776iysnJQBRQXF6u9vd1/HDlyZFDXAwAMHB+8AgAAAAAAWGNAzfnCwkJt375de/fu1bhx4/zjGRkZ6u7uVltbW8D81tZWZWRk9Hsth8OhpKSkgAMAEB588AoAAAAAAGCNoJrzxhgVFhaqqqpKe/bs0YQJEwJez87OVmxsrGpqavxjzc3NamlpkcvlGpqKAQAhwQevAAAAAAAA1gnqC2HdbrcqKir00ksvKTEx0b+dgdPp1OjRo+V0OrV8+XIVFRUpJSVFSUlJWrFihVwuF18YCADDlDFGK1asUFVVlWpra8/5wWt+fr4kPngFMPKNX70jbPc+vG5e2O4NAAAAYPgIqjm/adMmSdKcOXMCxsvKyrR06VJJ0oYNGzRq1Cjl5+fL5/MpLy9PGzduHJJiAQBDjw9eAQAAAAAArBdUc94Yc9458fHx8ng88ng8Ay4KAGAdPngFAAAAAACwXlDNeQBA5OGDVwAAAAAAAOsF9YWwAAAAAAAAAABg8GjOAwAAAAAAAABgMZrzAAAAAAAAAABYjD3nAeA8xq/eEbZ7H143L2z3BgAAAAAAQOjw5DwAAAAAAAAAABajOQ8AAAAAAAAAgMVozgMAAAAAAAAAYDGa8wAAAAAAACPAvn37NH/+fI0dO1ZRUVF68cUXA143xuiBBx7QmDFjNHr0aOXm5uqDDz4IT7EAgPOiOQ8AAAAAADACdHZ26pprrpHH4+n39ccee0xPPfWUnnnmGb3++uu68MILlZeXp66uLosrBQB8GTHhLgAAAAAAAADnN3fuXM2dO7ff14wxeuKJJ/TjH/9Yt912myTpl7/8pdLT0/Xiiy/qjjvuOOM9Pp9PPp/Pf97R0RGawgEA/eLJeQAAAAAAgBHu0KFD8nq9ys3N9Y85nU5Nnz5d9fX1/b6npKRETqfTf2RmZlpVLgBANOcBAACAiMS+xABgL16vV5KUnp4eMJ6enu5/7YuKi4vV3t7uP44cORLyOgEAn6M5DwAAAEQg9iUGAJyPw+FQUlJSwAEAsA57zgMAAAARiH2JAcBeMjIyJEmtra0aM2aMf7y1tVXTpk0LU1UAgHPhyXkAAADAZtiXGAAiz4QJE5SRkaGamhr/WEdHh15//XW5XK4wVgYAOBua8wAAAIDNsC8xAIxMn3zyiZqamtTU1CTp5IetTU1NamlpUVRUlFauXKlHHnlEv/vd77R//37dddddGjt2rBYsWBDWugEA/WNbGwAAAADn5XA45HA4wl0GANjam2++qZtvvtl/XlRUJElasmSJysvL9YMf/ECdnZ2655571NbWpptuuknV1dWKj48PV8kAgHOgOQ8AAADYDPsSA8DINGfOHBljzvp6VFSUHn74YT388MMWVgUAGCi2tQEAAABshn2JAQAAgPDjyXkAAAAgAn3yySf68MMP/een9iVOSUlRVlaWf1/ir3zlK5owYYLuv/9+9iUGAAAALERzHgAAAIhA7EsMAAAADG805wEAAIAIxL7EAAAAwPDGnvMAAAAAAAAAAFiM5jwAAAAAAAAAABajOQ8AAAAAAAAAgMVozgMAAAAAAAAAYDGa8wAAAAAAAAAAWIzmPAAAAAAAAAAAFqM5DwAAAAAAAACAxWjOAwAAAAAAAABgMZrzAAAAAAAAAABYjOY8AAAAAAAAAAAWozkPAAAAAAAAAIDFaM4DAAAAAAAAAGAxmvMAAAAAAAAAAFiM5jwAAAAAAAAAABajOQ8AAAAAAAAAgMVozgMAAAAAAAAAYDGa8wAAAAAAAAAAWIzmPAAAAAAAAAAAFqM5DwAAAAAAAACAxWjOAwAAAAAAAABgMZrzAAAAAAAAAABYjOY8AAAAAAAAAAAWC1lz3uPxaPz48YqPj9f06dP1+9//PlS3AgBYhGwHgMhDtgNA5CHbAWBkCElzfvPmzSoqKtKaNWv01ltv6ZprrlFeXp6OHTsWitsBACxAtgNA5CHbASDykO0AMHKEpDm/fv16ffvb39ayZct05ZVX6plnntEFF1yg5557LhS3AwBYgGwHgMhDtgNA5CHbAWDkiBnqC3Z3d6uxsVHFxcX+sVGjRik3N1f19fVnzPf5fPL5fP7z9vZ2SVJHR8eAa+jzfTrg9wLAcDKYLDz1XmPMoOsg2wFg6JDtnyPbAUQKsv1zZDuASGFFtg95c/748ePq7e1Venp6wHh6erref//9M+aXlJTooYceOmM8MzNzqEsDgBHH+cTgr3HixAk5nc5BXYNsB4ChQ7YDQOQh2wEg8liR7UPenA9WcXGxioqK/Od9fX36v//7P6WmpioqKiro63V0dCgzM1NHjhxRUlLSUJY6rNlx3XZcs8S6WfeXZ4zRiRMnNHbs2BBVd3Zk+9Cw47rtuGaJdbPuL49sH9nsuGaJdbPuyDfYNUdKttvx715i3azbHuy4bquyfcib8xdffLGio6PV2toaMN7a2qqMjIwz5jscDjkcjoCx5OTkQdeRlJRkm39ZTmfHddtxzRLrtpuBrnuwT96cQraHlx3Xbcc1S6zbbsj2k+z492/HNUus227suO7BrDmSst2Of/cS67Yb1m0foc72If9C2Li4OGVnZ6umpsY/1tfXp5qaGrlcrqG+HQDAAmQ7AEQesh0AIg/ZDgAjS0i2tSkqKtKSJUt03XXX6YYbbtATTzyhzs5OLVu2LBS3AwBYgGwHgMhDtgNA5CHbAWDkCElzvqCgQB9//LEeeOABeb1eTZs2TdXV1Wd8IUkoOBwOrVmz5oxfy4p0dly3HdcssW7WHT5ku/XsuG47rlli3aw7fMh2a9lxzRLrZt2Rb7itOVzZPtz+HKzCulm3Hdhx3VatOcoYY0J6BwAAAAAAAAAAEGDI95wHAAAAAAAAAADnRnMeAAAAAAAAAACL0ZwHAAAAAAAAAMBiNOcBAAAAAAAAALDYiGzOezwejR8/XvHx8Zo+fbp+//vfn3P+li1bNGnSJMXHx+vqq6/Wzp07Lap06ASz5meffVazZs3SRRddpIsuuki5ubnn/TMaroL9uz6lsrJSUVFRWrBgQWgLDJFg193W1ia3260xY8bI4XDo8ssvj/h/zyXpiSee0BVXXKHRo0crMzNTq1atUldXl0XVDt6+ffs0f/58jR07VlFRUXrxxRfP+57a2lpde+21cjgcuuyyy1ReXh7yOq1ix2yX7JnvZDvZfi4jPdsl8v10dsx2O+a6RLaT7ZGd7eT65+yY6xLZTrZHfrbbLdelYZTtZoSprKw0cXFx5rnnnjPvvvuu+fa3v22Sk5NNa2trv/NfffVVEx0dbR577DFz4MAB8+Mf/9jExsaa/fv3W1z5wAW75kWLFhmPx2Pefvtt895775mlS5cap9Np/vSnP1lc+eAEu+5TDh06ZC655BIza9Ysc9ttt1lT7BAKdt0+n89cd9115tZbbzWvvPKKOXTokKmtrTVNTU0WVz44wa77+eefNw6Hwzz//PPm0KFDZteuXWbMmDFm1apVFlc+cDt37jT33Xef2bp1q5Fkqqqqzjn/4MGD5oILLjBFRUXmwIED5umnnzbR0dGmurramoJDyI7Zbow9851sJ9sjPduNId9PsWO22zHXjSHbyfbIz3Zy/SQ75roxZDvZHvnZbsdcN2b4ZPuIa87fcMMNxu12+897e3vN2LFjTUlJSb/zb7/9djNv3ryAsenTp5t//Md/DGmdQynYNX/RZ599ZhITE80vfvGLUJUYEgNZ92effWZuvPFG82//9m9myZIlI/IHQbDr3rRpk7n00ktNd3e3VSWGRLDrdrvdJicnJ2CsqKjIzJw5M6R1hsqX+UHwgx/8wFx11VUBYwUFBSYvLy+ElVnDjtlujD3znWw/iWy3R7YbY+98t2O22zHXjSHbTyHb7ZHt5Lq9ct0Ysv0Usj1ys93uuW5MeLN9RG1r093drcbGRuXm5vrHRo0apdzcXNXX1/f7nvr6+oD5kpSXl3fW+cPNQNb8RZ9++ql6enqUkpISqjKH3EDX/fDDDystLU3Lly+3oswhN5B1/+53v5PL5ZLb7VZ6erqmTJmitWvXqre316qyB20g677xxhvV2Njo/1WrgwcPaufOnbr11lstqTkcRnqenY0ds12yZ76T7WQ72d6/SMi0L7Jjttsx1yWynWwn2/sz0vOsP3bMdYlsJ9sjP9vJ9S8vVJkWM6h3W+z48ePq7e1Venp6wHh6erref//9ft/j9Xr7ne/1ekNW51AayJq/6Ic//KHGjh17xr9Aw9lA1v3KK6+otLRUTU1NFlQYGgNZ98GDB7Vnzx7deeed2rlzpz788EN973vfU09Pj9asWWNF2YM2kHUvWrRIx48f10033SRjjD777DN95zvf0Y9+9CMrSg6Ls+VZR0eH/vKXv2j06NFhqmxw7Jjtkj3znWwn2yWyvT+RmO92zHY75rpEtpPtJ5Htgcj1k0Z6rktkO9l+UiRnO7n+5YUq20fUk/MI3rp161RZWamqqirFx8eHu5yQOXHihBYvXqxnn31WF198cbjLsVRfX5/S0tL085//XNnZ2SooKNB9992nZ555JtylhVRtba3Wrl2rjRs36q233tLWrVu1Y8cO/eQnPwl3aYAl7JDvZDvZTrbDTuyQ6xLZTraT7bAXsj3y2THbyfWhNaKenL/44osVHR2t1tbWgPHW1lZlZGT0+56MjIyg5g83A1nzKY8//rjWrVun//zP/9TUqVNDWeaQC3bd//M//6PDhw9r/vz5/rG+vj5JUkxMjJqbmzVx4sTQFj0EBvL3PWbMGMXGxio6Oto/NnnyZHm9XnV3dysuLi6kNQ+Fgaz7/vvv1+LFi3X33XdLkq6++mp1dnbqnnvu0X333adRoyLvs8ez5VlSUtKIfPrmFDtmu2TPfCfbyXaJbO9PJOa7HbPdjrkuke1k+0lkeyBy/aSRnusS2U62nxTJ2U6uf3mhyvYR9acVFxen7Oxs1dTU+Mf6+vpUU1Mjl8vV73tcLlfAfEnavXv3WecPNwNZsyQ99thj+slPfqLq6mpdd911VpQ6pIJd96RJk7R//341NTX5j29961u6+eab1dTUpMzMTCvLH7CB/H3PnDlTH374of8HnyT94Q9/0JgxY4b9D4FTBrLuTz/99IzAP/XD8OR3eUSekZ5nZ2PHbJfsme9kO9lOtvcvEjLti+yY7XbMdYlsJ9vJ9v6M9Dzrjx1zXSLbyfbIz3Zy/csLWaYN6utkw6CystI4HA5TXl5uDhw4YO655x6TnJxsvF6vMcaYxYsXm9WrV/vnv/rqqyYmJsY8/vjj5r333jNr1qwxsbGxZv/+/eFaQtCCXfO6detMXFyceeGFF8xHH33kP06cOBGuJQxIsOv+opH6zeDBrrulpcUkJiaawsJC09zcbLZv327S0tLMI488Eq4lDEiw616zZo1JTEw0v/nNb8zBgwfNf/zHf5iJEyea22+/PVxLCNqJEyfM22+/bd5++20jyaxfv968/fbb5o9//KMxxpjVq1ebxYsX++cfPHjQXHDBBeb73/++ee+994zH4zHR0dGmuro6XEsYMnbMdmPsme9kO9ke6dluDPl+ih2z3Y65bgzZTrZHfraT6yfZMdeNIdvJ9sjPdjvmujHDJ9tHXHPeGGOefvppk5WVZeLi4swNN9xgGhoa/K999atfNUuWLAmY/9vf/tZcfvnlJi4uzlx11VVmx44dFlc8eMGs+a//+q+NpDOONWvWWF/4IAX7d326kfqDwJjg1/3aa6+Z6dOnG4fDYS699FLz6KOPms8++8ziqgcvmHX39PSYBx980EycONHEx8ebzMxM873vfc/8+c9/tr7wAdq7d2+//1s9tc4lS5aYr371q2e8Z9q0aSYuLs5ceumlpqyszPK6Q8WO2W6MPfOdbCfbT4nEbDeGfD+dHbPdjrluDNlOtkd2tpPrn7NjrhtDtpPtkZ/tdst1Y4ZPtkcZE8G/bwAAAAAAAAAAwDA0ovacBwAAAAAAAAAgEtCcBwAAAAAAAADAYjTnAQAAAAAAAACwGM15AAAAAAAAAAAsRnMeAAAAAAAAAACL0ZwHAAAAAAAAAMBiNOcBAAAAAAAAALAYzXkAAAAAAAAAACxGcx4AAAAAAAAAAIvRnAcAAAAAAAAAwGI05wEAAAAAAAAAsBjNeQAAACBCjB8/XkuXLg13GQAAAAC+BJrzAAAAAAAAAABYjOY8AAAAAAAAAAAWozkPAAAAAAAAAIDFaM4DAAAAQ+zBBx9UVFSU3n//fd1+++1KSkpSamqq7r33XnV1dUmSpkyZoptvvvmM9/b19emSSy7R3/3d3/nHHn/8cd14441KTU3V6NGjlZ2drRdeeOFL1/FF5eXlioqK0uHDhwPGX375Zc2aNUsXXnihEhMTNW/ePL377rtBrh4AAADAl0FzHgAAAAiR22+/XV1dXSopKdGtt96qp556Svfcc48kqaCgQPv27ZPX6w14zyuvvKKjR4/qjjvu8I89+eST+pu/+Rs9/PDDWrt2rWJiYvT3f//32rFjx5DV+qtf/Urz5s1TQkKCfvrTn+r+++/XgQMHdNNNN53RxAcAAAAweDHhLgAAAACIVBMmTNBLL70kSXK73UpKStLGjRv1z//8zyooKNADDzygF154QYWFhf73bN68WQkJCZo3b55/7A9/+INGjx7tPy8sLNS1116r9evXB8wbqE8++UT/9E//pLvvvls///nP/eNLlizRFVdcobVr1waMAwAAABg8npwHAAAAQsTtdgecr1ixQpK0c+dOXX755Zo2bZo2b97sf723t1cvvPCC5s+fH9CMP/2f//znP6u9vV2zZs3SW2+9NSR17t69W21tbfqHf/gHHT9+3H9ER0dr+vTp2rt375DcBwAAAMDneHIeAAAACJGvfOUrAecTJ07UqFGj/NvEFBQU6Ec/+pH+93//V5dccolqa2t17NgxFRQUBLxv+/bteuSRR9TU1CSfz+cf728/+YH44IMPJEk5OTn9vp6UlDQk9wEAAADwOZrzAAAAgEW+2EwvKChQcXGxtmzZopUrV+q3v/2tnE6nvvGNb/jn/Nd//Ze+9a1vafbs2dq4caPGjBmj2NhYlZWVqaKiIqj7ndLb2xtw3tfXJ+nkvvMZGRlnzI+J4f82AAAAAEON/8oGAAAAQuSDDz7QhAkT/Ocffvih+vr6NH78eEkn96S/4YYbtHnzZhUWFmrr1q1asGCBHA6H/z3//u//rvj4eO3atStgvKys7Lz3v+iiiyRJbW1tSk5O9o//8Y9/DJg3ceJESVJaWppyc3ODXicAAACA4LHnPAAAABAiHo8n4Pzpp5+WJM2dO9c/VlBQoIaGBj333HM6fvz4GVvaREdHKyoqKuBp98OHD+vFF1887/1PNd337dvnH+vs7NQvfvGLgHl5eXlKSkrS2rVr1dPTc8Z1Pv744/PeCwAAAEBwoowxJtxFAAAAAJHkwQcf1EMPPaSrr75a48eP1ze+8Q3V19fr17/+tRYtWqTnn3/eP/dPf/qTsrKylJCQoNjYWHm9XsXGxvpf37Nnj772ta9p1qxZWrRokY4dOyaPx6OMjAz993//t07/z/nx48drzpw5Ki8vlyT19PTosssu06effqrvf//7io6O1nPPPafRo0ersbFRhw4d8j/FX1FRocWLF+vKK6/UHXfcob/6q79SS0uLduzYoZkzZ+pnP/uZJX92AAAAgF3w5DwAAAAQIps3b5bD4dDq1au1Y8cOFRYWqrS0NGDOuHHjdOONN+rEiRNauHBhQGNeOvklraWlpfJ6vVq5cqV+85vf6Kc//an+9m//9rz3j42NVVVVlSZOnKj7779fTz31lO6++24VFhaeMXfRokWqqanRJZdcon/5l3/Rvffeq8rKSk2bNk3Lli0b3B8EAAAAgDPw5DwAAAAwxE49Of/xxx/r4osvDnc5AAAAAIYhnpwHAAAAAAAAAMBiNOcBAAAAAAAAALAYzXkAAAAAAAAAACzGnvMAAAAAAAAAAFiMJ+cBAAAAAAAAALAYzXkAAAAAAAAAACxGcx4AAAAAAAAAAIvRnAcAAAAAAAAAwGI05wEAAAAAAAAAsBjNeQAAAAAAAAAALEZzHgAAAAAAAAAAi9GcBwAAAAAAAADAYv8Pv+b6Vk8C6WMAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(2, 4, figsize=(15, 6), constrained_layout=True)\n", "for i, (ni, vi) in enumerate(pvalues.items()):\n", " ax[0, i].hist(vi[:, 0])\n", " ax[1, i].hist(vi[:, 1])\n", " ax[0, i].set_title(f\"n = {ni}, failed fits = {np.sum(np.isnan(vi[:, 0]))}\")\n", " ax[1, i].set_title(f\"n = {ni}, failed fits = {np.sum(np.isnan(vi[:, 1]))}\")\n", "fig.supxlabel(\"pvalue\");" ] } ], "metadata": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" }, "kernelspec": { "display_name": "Python 3.8.12 ('venv': venv)", "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.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)]" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/hesse_and_minos.ipynb0000644000000000000000000073125414332717401017172 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Hesse and Minos\n", "\n", "We discuss how uncertainty computation with Hesse and Minos works and how the two approaches differ.\n", "\n", "iminuit (and C++ MINUIT) offers two major ways of computing parameter uncertainties from the cost function (which must be of least-squares form or negative log-likelihood), HESSE and MINOS. These have different pros and cons, on which we already touched in the basic tutorial. Here we want to go a bit deeper into the pros and cons and also try to reveal a bit of the math behind the two approaches.\n", "\n", "There are several sources which explain what HESSE and MINOS do, in particular the MINUIT User's Guide (linked from the [iminuit documentation](https://iminuit.readthedocs.io/en/latest/about.html)). The mathematical details are covered in F. James, \"Statistical Methods in Experimental Physics\", 2nd edition, World Scientific (2006)." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We need to use a couple of technical terms from mathematical statistics when talk about proven properties. We also use recurring variable names with the same meaning throughout.\n", "\n", "* $n$ number of [observations](https://en.wikipedia.org/wiki/Sample_%28statistics%29)\n", "* $x$ observation from data distribution $f(x; \\theta)$ with parameter $\\theta$ (not an angle!)\n", "* $\\hat \\theta$ estimate of $\\theta$ obtained from a sample (the parameter value obtained from the fit)\n", "* $\\mathcal L$ likelihood function\n", "* $\\mathcal I$ [Fisher information](https://en.wikipedia.org/wiki/Fisher_information)\n", "* $E[\\dots]$ expectation value of $\\dots$ over distribution of $x$\n", "* $V[\\dots]$ variance of $\\dots$\n", "\n", "The terms **asymptotic** and **asymptotic limit** refer to inference from an infinite data sample. Mathematical properties of statistical methods are most commonly computed in this limit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## HESSE\n", "\n", "HESSE in a nutshell:\n", "\n", "* Computes approximate covariance matrix $C_{ij}$ for all fitted parameters\n", "* Square-root of diagonal elements of $C_{ij}$ corresponds to one standard deviation; to obtain $k$ standard deviations, multiply covariance matrix $C$ by $k^2$\n", "* Can be used to form confidence intervals $\\hat \\theta_i \\pm C_{ii}^{1/2}$\n", "* Only way to obtain parameter correlations\n", "* Comparably fast, requires:\n", " * Numerical computation of all second derivatives of the cost function (Hessian matrix)\n", " * Inverting this matrix\n", "* Cannot compute uncertainty for only one parameter if there are several\n", "* Approximately computed as by-product of MIGRAD algorithm (usually accurate enough when `strategy` >= 1 is used)\n", "\n", "The algorithm derives its name from the Hessian matrix of second derivatives which is computed to obtain the uncertainties.\n", "\n", "### How does it work?\n", "\n", "The matrix of second derivatives of the cost function is computed for all free parameters and multiplied by 2. The result is inverted and multiplied by `errordef`.\n", "\n", "MINUIT implements parameter limits by applying a variable transformation and therefore distinguishes internal and external parameter space. The Hessian matrix is computed in the internal parameter space and transformed using the chain rule to external space.\n", "\n", "### Why does it work?\n", "\n", "One can prove under general conditions in the asymptotic limit that a parameter estimate $\\hat\\theta$ obtained from the least-squares and the maximum-likelihood methods is normally distributed with minimum variance, given by the [Cramer-Rao lower bound](https://en.wikipedia.org/wiki/Cram%C3%A9r%E2%80%93Rao_bound), which is the minimum for any unbiased estimator (these methods are asymptotically unbiased).\n", "\n", "$$\n", "V(\\hat \\theta) \\underset{n\\rightarrow \\infty}{\\longrightarrow} \\left\\{ n E\\left[\\left( \\frac{\\partial \\ln\\! f(x;\\theta)}{\\partial \\theta} \\right)^2 \\right]_{\\theta = \\hat\\theta} \\right\\}^{-1} = \\left\\{ -n E\\left[\\frac{\\partial^2 \\ln\\! f(x;\\theta)}{\\partial \\theta^2} \\right]_{\\theta = \\hat\\theta} \\right\\}^{-1}\n", "$$\n", "\n", "The expectation here is taken over the data distribution. Since the expectation value is constant, we see that the variance of $\\hat\\theta$ scales goes down with $n^{-1}$ in the asymptotic limit.\n", "\n", "If the data range is independent of $\\theta$, which we usually assume (but see F. James book for a counter example), we can swap integration over $x$ and differentiation with respect to $\\theta$. Doing this and replacing the expectation with its plug-in estimate, the arithmetic average, we obtain:\n", "\n", "$$\n", "-n E\\left[\\frac{\\partial^2 \\ln\\! f(x;\\theta)}{\\partial \\theta^2} \\right]_{\\theta = \\hat \\theta} = -n \\frac{1}{n} \\sum_i \\frac{\\partial^2 \\ln\\! f(x_i; \\theta)}{\\partial \\theta^2}\\Big\\vert_{\\theta = \\hat \\theta} = \\frac{\\partial^2 \\big(-\\sum_i \\ln\\! f(x_i; \\theta)\\big)}{\\partial \\theta^2}\\Big\\vert_{\\theta = \\hat \\theta}\n", "$$\n", "\n", "We now see that the numerator contains the negative log-likelihood function that we often plug into iminuit. If there is a vector of parameters $\\hat{\\vec \\theta}$, then this turns into the Hessian matrix of second derivatives.\n", "\n", "#### A bit of history\n", "\n", "So what about these factors of $2$ and `errordef` in MINUIT that were mentioned above? These don't appear here. There are historical reasons for those. In the asymptotic limit, the least-squares cost function that corresponds to the log-likelihood is $Q = -2 \\ln\\! \\mathcal{L} - \\text{constants}$. MINUIT was originally developed with least-squares fits in mind, therefore its internal math assumes the $Q$ form. If the second derivatives are computed from $Q$, the constants are removed but the Hessian must be multiplied by a factor of two to get the right variance. Correspondingly, if the user puts in a negative log-likelihood function, the same procedure now introduces an extra factor of two $2$, which must be compensated by the `errordef` value of 0.5 for the negative log-likelihood.\n", "\n", "### Why is HESSE approximate\n", "\n", "We started out from Cramer-Rao bound, the asymptotic limit for the parameter variance. How fast the finite sample approaches the limit depends on the problem at hand. For normal distributed data, the bound is exact.\n", "\n", "We further approximated the computation of the bound by replacing the expectation over the likelihood with the sample mean of the likelihood. \n", "\n", "## MINOS\n", "\n", "MINOS in a nutshell:\n", "\n", "* Approximate confidence intervals (intervals are wrongly claimed to be \"exact\" in some sources, including the MINUIT paper from 1975)\n", "* Cannot compute parameter correlations\n", "* Some (unverified) sources claim better coverage probability than intervals based on HESSE\n", "* In general slower than HESSE (requiring more function evaluations):\n", " * Iteratively computes the value pair $\\theta^d, \\theta^u$ which increases the cost function by `errordef` over the minimum\n", " * If the cost function has several parameters, it is minimised with respect to all other parameters during this scan\n", "* Can be used to compute uncertainty for only one parameter - but this is not more efficient than HESSE, since the computation requires at least one evaluation of HESSE\n", "\n", "The MINOS algorithm computes [profile-likelihood based](https://en.wikipedia.org/wiki/Likelihood_function#Profile_likelihood) confidence intervals.\n", "\n", "### How does it work?\n", "\n", "MINOS scans the likelihood along one parameter $\\theta_i$, while minimizing the likelihood with respect to all other parameters $\\theta_k$ with $k \\ne i$. This is effectively the same as expressing the other parameter estimates as a function of $\\theta_i$, $\\hat \\theta_k(\\theta_i)$, and scanning the now one-dimensional negative log-likelihood $-\\ln \\mathcal{L}(\\theta_i; \\theta_k = \\hat \\theta_k(\\theta_i) , k\\ne i)$. This is called the [profile likelihood](https://en.wikipedia.org/wiki/Likelihood_function#Profile_likelihood) for parameter $\\theta_i$.\n", "\n", "One follows this curve until $-\\ln \\mathcal{L}$ increases by `errordef` with respect to its minimum and stores the two corresponding values $\\theta^d_i$ and $\\theta^u_i$. The interval $(\\theta^d_i, \\theta^u_i)$ has $68\\,\\%$ coverage probability in the asymptotic limit. In multi-dimensional parameter space, the computation is comparably expensive due to the iterative steps of scanning and minimization.\n", "\n", "An efficient algorithm to compute the interval is described by Venzon and Moolgavkar in *A Method for Computing Profile-Likelihood-Based Confidence Intervals*, Journal of the Royal Statistical Society C37 (1988) 87–94 [DOI](https://doi.org/10.2307%2F2347496).\n", "\n", "### Why does it work?\n", "\n", "We define the likelihood ratio $\\ell(\\vec\\theta) = \\mathcal{L}(\\vec\\theta) / \\mathcal{L}(\\hat{\\vec\\theta})$. In the asymptotic limit, $-2 \\ln \\ell(\\vec\\theta)$ is $\\chi^2(k)$ distributed, where $k$ is the number of fitted parameters. For $k = 1$, the $\\chi^2$-interval $[0, 1)$ contains $68\\,\\%$ probability. For a single parameter fit therefore a corresponding interval is found by the two values $\\theta^d$ and $\\theta^u$ which solve\n", "\n", "$$\n", "-2\\ln\\ell(\\theta) = 1 \\Leftrightarrow -\\ln\\ell(\\theta) = 1/2\n", "$$\n", "\n", "We recognize the difference in the negative log-likelihood on the left-hand side and our `errordef = 0.5` on the right-hand side. Confidence intervals with other coverage probability can be constructed by finding the corresponding upper value of the $\\chi^2$-interval with that integrated probability. In a least-squares fit, we have\n", "\n", "$$\n", "\\Delta Q(\\theta) = -2 \\ln \\ell(\\theta) = 1\n", "$$\n", "\n", "therefore `errordef = 1` is the crossing value for a least-squares cost function.\n", "\n", "In the multi-parameter case, when we search for the interval for $\\theta_i$, the other parameters are effectively fixed to their best-fit values for the current value $\\theta_i$ in the scan, $\\theta_k = \\hat\\theta_k(\\theta_i)$. Therefore, during the scan they are not free. The profile likelihood is effectively a likelihood for a single parameter. Thus, $68\\,\\%$-intervals are also here obtained when the negative log-likelihood crosses the value $1/2$ above the minimum. \n", "\n", "### Why is MINOS approximate\n", "\n", "In some sources, the MINOS intervals are wrongly described as *exact*. They are not exact, because for finite samples the intervals do not necessarily have $68\\,\\%$ coverage probability, only in the asymptotic limit.\n", "\n", "Some sources claim that two approximations are involved in the HESSE calculation of an interval, but only one in the MINOS calculation and conclude that MINOS intervals therefore approach the asymptotic limit more rapidly. This claim is disputed by others." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Coverage probability\n", "\n", "We compute the coverage probability of HESSE and MINOS intervals in toy experiments.\n", "\n", "### Poisson distributed data\n", "\n", "We construct HESSE and MINOS intervals for a counting experiment. We consider the extreme case of a single observation $k$ drawn from a Poisson distribution $P(k;\\lambda)$. We use the maximum-likelihood method to find the best estimate for $\\lambda$ for each $k$ under the constraint $\\lambda > 0$, which is trivially just $\\hat \\lambda = k$, and construct intervals with the HESSE and MINOS algorithms to check their coverage.\n", "\n", "This case can be fully handled analytically, but here we use iminuit's HESSE and MINOS algorithms to compute the intervals." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from matplotlib import pyplot as plt\n", "from scipy.stats import multivariate_normal, poisson\n", "from argparse import Namespace\n", "from iminuit import Minuit\n", "from functools import lru_cache\n", "import joblib" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "@lru_cache(10000)\n", "def run(k):\n", " m = Minuit(lambda lambd: -poisson.logpmf(k, lambd), lambd=k + 1)\n", " m.limits[\"lambd\"] = (0, None)\n", " m.errordef = Minuit.LIKELIHOOD\n", " m.migrad()\n", " m.hesse()\n", " m.minos()\n", " assert m.valid\n", "\n", " p = m.values[\"lambd\"]\n", " dp = m.errors[\"lambd\"]\n", " pm = max(p + m.merrors[\"lambd\"].lower, 0.0), p + m.merrors[\"lambd\"].upper\n", "\n", " r = p, dp, *pm\n", " return r\n", "\n", "\n", "rng = np.random.default_rng(seed=1)\n", "nmc = 5000\n", "mu = 10 ** np.linspace(-1, 2, 100)\n", "\n", "pcov = {\n", " \"HESSE\": np.empty_like(mu),\n", " \"MINOS\": np.empty_like(mu),\n", "}\n", "\n", "for i, mui in enumerate(mu):\n", "\n", " nh = 0\n", " nm = 0\n", " for imc in range(nmc):\n", " k = rng.poisson(mui)\n", "\n", " p, dp, pd, pu = run(k)\n", "\n", " if p - dp < mui < p + dp:\n", " nh += 1\n", " if pd < mui < pu:\n", " nm += 1\n", "\n", " pcov[\"HESSE\"][i] = nh / nmc\n", " pcov[\"MINOS\"][i] = nm / nmc\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(10, 4))\n", "\n", "plt.sca(ax[0])\n", "n = np.arange(101)\n", "interval = {\n", " \"HESSE\": np.empty((len(n), 2)),\n", " \"MINOS\": np.empty((len(n), 2)),\n", "}\n", "for i, k in enumerate(n):\n", " p, dp, pd, pu = run(k)\n", " interval[\"HESSE\"][i] = (p - dp, p + dp)\n", " interval[\"MINOS\"][i] = (pd, pu)\n", "\n", "for algo, vals in interval.items():\n", " plt.plot(n, vals[:, 0] - n, color=\"C0\" if algo == \"HESSE\" else \"C1\", label=algo)\n", " plt.plot(n, vals[:, 1] - n, color=\"C0\" if algo == \"HESSE\" else \"C1\", label=algo)\n", "plt.xlabel(\"$k$\")\n", "plt.ylabel(r\"$\\lambda^d - \\hat\\lambda$, $\\lambda^u - \\hat\\lambda$\")\n", "\n", "plt.sca(ax[1])\n", "for algo, vals in pcov.items():\n", " plt.plot(mu, vals, label=algo)\n", "\n", "plt.axhline(0.68, ls=\":\", color=\"0.5\", zorder=0)\n", "plt.xlabel(r\"$\\lambda$\")\n", "plt.ylabel(\"coverage probability\")\n", "plt.legend()\n", "plt.semilogx();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "In this special case, the intervals found by both methods are very close. The MINOS interval is identical to the HESSE interval up to a small almost constant shift. Visually, the rate of converge to the asymptotic coverage probability of 68% seems to be equal for both methods." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We can speak about the rate of convergence although we have drawn only a single observation from the Poisson pdf. The log-likelihood for $n$ observations with the same expectation is identical to the log-likelihood for one observation with an $n$-times higher expectation up to additive constants.\n", "\n", "$$\n", "\\ln L = \\sum_i^n (\\lambda_i - k_i \\ln \\lambda_i)\n", "= \\lambda - \\sum_i k_i (\\ln \\lambda - \\ln n)\n", "= \\lambda - k \\ln \\lambda + k \\ln n\n", "$$\n", "\n", "with $\\sum_i^n \\lambda_i = \\lambda$, $\\sum_i^n k_i = k$, and $\\lambda_i = \\lambda / n$. Therefore, the test cases with large values of $\\lambda$ correspond to large observation samples with a small constant $\\lambda$." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Fit of transformed normally distributed data" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 3.122 Nfcn = 168
EDM = 2.02e-05 (Goal: 0.0001)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 3.122 │ Nfcn = 168 │\n", "│ EDM = 2.02e-05 (Goal: 0.0001) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 cx 0.11 0.06 -0.08 0.05
1 cy 0.05 0.10 -0.11 0.08
" ], "text/plain": [ "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ cx │ 0.11 │ 0.06 │ -0.08 │ 0.05 │ │ │ │\n", "│ 1 │ cy │ 0.05 │ 0.10 │ -0.11 │ 0.08 │ │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cx cy
Error -0.08 0.05 -0.11 0.08
Valid True True True True
At Limit False False False False
Max FCN False False False False
New Min False False False False
" ], "text/plain": [ "┌──────────┬───────────────────────┬───────────────────────┐\n", "│ │ cx │ cy │\n", "├──────────┼───────────┬───────────┼───────────┬───────────┤\n", "│ Error │ -0.08 │ 0.05 │ -0.11 │ 0.08 │\n", "│ Valid │ True │ True │ True │ True │\n", "│ At Limit │ False │ False │ False │ False │\n", "│ Max FCN │ False │ False │ False │ False │\n", "│ New Min │ False │ False │ False │ False │\n", "└──────────┴───────────┴───────────┴───────────┴───────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "(-0.1, 0.2)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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rbfearVHa2ewsbHh5ZdfZs6cOdx9993ExMTwwAMPMGrUKEaMGNEr92QWncdHZ8crvx/GM98m06woWGg0vPz7oe1u8tWyNjkpKYn169ezbt06U+M6CwuLK66zrKyMO++8k+LiYnQ6HcOHD+fnn3/mmmuuAWDFihUsXbrUdPzkyZOB//3O29jYsHbtWp577jkaGhro168fCxcubLWOWQghhBBdQxp8db7G+kae+91rlBdW0HeQH39f85cOeU/Wk2gUWfTardXX1/Pcc8/x7rvvmkaZR44ciZ+fn9qldXsGg4GNGzdy3XXX9foGX3B67XJueS1B7vbtDspnjiYvX74cd3d3Vq9efcWjyUIIIdpOr9ej0+lMW2EKYc6So9NZOGkxzp46viz6CK22164e7RSKovDqXe+x7Ys9OLr04b24V/Dr3322qO2qf8/kb103Z2try7Jly9ixYwfbt29n8eLF/Pjjj8THx3dokzEhfHR2RIW4tSsot4wmb9682bQ2+YEHHmDv3r0SlIUQQghxQQPHhGDnYEtlWRXZh/LULqfH+fK1H9j2xR60FloWf/14twrKXUnCcg8RGRlJUlISM2bM4LHHHjN1zC4sLFS7NNFL1dbWEhMTww8//MCiRYvIyMggISGBJ554Qqb4CCGEEOKirKytGDF1CAAJWw6pXE3PsuvrWD55Zg0AC979MyOvHqZyReZLwnIPYmtry6uvvsr27dvZtm0bzz77LD/++CP79++ntvbyOxcL0R5Go5GjR4+yefNm05Zn9913H9HR0QwaNEjt8oQQQgjRTURcMwKAA1sOqltID7Lnm328/H//RFEUZj80k9kPzVS7JLPWaxt89WQto8zPPvssjz32GH/605+4+uqrCQsLY8CAAdIATHQKRVE4duwYaWlpJCQksGrVKlxcXDhw4MBl75sshBBCiN5r9IzTYTllbzp1NfXY9bn0Nq7iwvZ+F8dLt/0TY7OR6XdMZv6796hdktmTkeUeytbWltdee43t27eTkpLCggULWL16NZs2bSIjI4Ompia1SxQ9hKIolJaWsnPnTr755huWLFnCm2++yfz584mOjpagLIQQQojL4hfqg3eQB4bGJg7tSlW7nG5t73dxvHjL2zQ3NTPt9kk88cnDsiyuDWRkuYeLjIxkz549/PjjjyxatIhvvvmGO+64g6ioKAYNGkRQUJB0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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "rng = np.random.default_rng(1)\n", "\n", "truth = Namespace(cr=0.1, cphi=0, sr=0.1, sphi=2)\n", "truth.cx = truth.cr * np.cos(truth.cphi)\n", "truth.cy = truth.cr * np.sin(truth.cphi)\n", "\n", "d_r = rng.normal(truth.cr, truth.sr, size=5)\n", "d_phi = rng.normal(truth.cphi, truth.sphi, size=d_r.shape)\n", "\n", "cov = np.eye(2)\n", "cov[0, 0] = truth.sr ** 2\n", "cov[1, 1] = truth.sphi ** 2\n", "\n", "\n", "def nll(cx, cy):\n", " cr = np.linalg.norm((cx, cy))\n", " cphi = np.arctan2(cy, cx)\n", " return -np.sum(\n", " multivariate_normal((cr, cphi), cov).logpdf(np.transpose((d_r, d_phi)))\n", " )\n", "\n", "\n", "m = Minuit(nll, cx=0.1, cy=0)\n", "m.errordef = Minuit.LIKELIHOOD\n", "m.migrad()\n", "m.hesse()\n", "m.minos()\n", "display(m.fmin, m.params)\n", "display(m.merrors)\n", "\n", "plt.figure(figsize=(14, 4))\n", "\n", "plt.subplot(121, polar=True)\n", "plt.plot(d_phi, d_r, \".\")\n", "\n", "plt.subplot(122, aspect=\"equal\")\n", "m.draw_mncontour(\"cx\", \"cy\", size=100)\n", "plt.plot(m.values[\"cx\"], m.values[\"cy\"], \"+k\")\n", "plt.xlim(-0.1, 0.2)\n", "plt.ylim(-0.1, 0.2)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "truth = Namespace(cr=0.1, cphi=0, sr=0.1, sphi=2)\n", "truth.cx = truth.cr * np.cos(truth.cphi)\n", "truth.cy = truth.cr * np.sin(truth.cphi)\n", "truth.cov = np.eye(2)\n", "truth.cov[0, 0] = truth.sr ** 2\n", "truth.cov[1, 1] = truth.sphi ** 2\n", "\n", "n_tries = 500 # increase this to 500 get smoother curves (running will take a while)\n", "n_data = np.unique(np.geomspace(5, 100, 20, dtype=int))\n", "\n", "\n", "@joblib.delayed\n", "def run(n):\n", " rng = np.random.default_rng(seed=n)\n", "\n", " n_h = 0\n", " n_m = 0\n", " h_lus = []\n", " m_lus = []\n", " xs = []\n", " for i_try in range(n_tries):\n", " while True:\n", " d_r = rng.normal(truth.cr, truth.sr, size=n)\n", " d_phi = rng.normal(truth.cphi, truth.sphi, size=n)\n", "\n", " def nll(cx, cy):\n", " cr = np.linalg.norm((cx, cy))\n", " cphi = np.arctan2(cy, cx)\n", " return -np.sum(\n", " multivariate_normal((cr, cphi), truth.cov).logpdf(\n", " np.transpose((d_r, d_phi))\n", " )\n", " )\n", "\n", " m = Minuit(nll, cx=0.1, cy=0)\n", " m.errordef = Minuit.LIKELIHOOD\n", " try:\n", " m.migrad()\n", " if not m.valid:\n", " continue\n", "\n", " m.hesse()\n", "\n", " if not m.accurate:\n", " continue\n", "\n", " m.minos(\"cx\")\n", " if m.merrors[\"cx\"].is_valid:\n", " break\n", "\n", " except Exception as e:\n", " print(f\"exception in n={n} i_try={i_try}\")\n", " print(e)\n", "\n", " x = m.values[\"cx\"]\n", " dx = m.errors[\"cx\"]\n", " me = m.merrors[\"cx\"]\n", " h_lu = x - dx, x + dx\n", " m_lu = x + me.lower, x + me.upper\n", " if h_lu[0] < truth.cx < h_lu[1]:\n", " n_h += 1\n", " if m_lu[0] < truth.cx < m_lu[1]:\n", " n_m += 1\n", " xs.append(x)\n", " h_lus.append(h_lu)\n", " m_lus.append(m_lu)\n", "\n", " x = np.mean(xs)\n", " h_l, h_u = np.mean(h_lus, axis=0)\n", " m_l, m_u = np.mean(m_lus, axis=0)\n", " return n_h, n_m, x, h_l, h_u, m_l, m_u\n", "\n", "\n", "n_h, n_m, x, hl, hu, ml, mu = np.transpose(joblib.Parallel(-1)(run(x) for x in n_data))\n", "\n", "h_pcov = n_h.astype(float) / n_tries\n", "m_pcov = n_m.astype(float) / n_tries" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/png": "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Vr94AHv37qV3797Vvz/66KM8+uijDBkyhKVLl57WPkVEGsrj9VJ4pIzCI6UUHC7h0GH/z9LySiJDXcREuImJCCEmIoRQlz1gU2XKyivZvu8gW/fks3VvPtv2HGDr3nxyDx6utb/dZqFD21g6Jsf6k6nkONq3jcFua1iZWrfDRufUeDqn1nwf9fl8HCoqOZ50Hf2577CPzMzMBh0rkLKzs/nd736H2+2uMfW7NrNmzWqiqM7cY489xn333Yfb7b9m59NPP+XDDz+kd+/eXHXVVdX9Hn30USorK5k4cWL1YslffPEF7733Ht27d+cnP/lJdd8nnniCkpISfvWrX1VXVFy9ejVvv/02nTt35rrrrqvuO2fOHAoLC7nllltISkoCYO3atbzxxhu0a9eOG264obrvs88+S15eHmPHjiUtLQ2ATZs2sWDBApKTk7npppuq+86bN4+9e/cyatQoOnXqBPhHHl966SXi4+O57bbbqvu+/PLL7Nixg2uuuYauXbsCsHv3bl588UWioqKYMGFCdd+FCxeyefNmrr76anr16gXA/v37+ctf/kJoaGiNv4033niDdevWcemll9K/f38ADh48yFNPPYXdbq+xpMvbb7/N119/zbBhwzjnnHMAOHz4MI8//jgmk4lp06ZV933vvff48ssvGTJkCBdccAHgL6Yyc+ZMAO67777qD3VLlixh+fLlDBo0iIsvvhjwf9A79kXz5MmTcTj810D+73//46OPPqJv375cfvnl1cebOXMmXq+XO++8k7CwMNJi3Nzaw87jX1Qxb9kO+qZFcWm3Nny+7QAP/vXfbK0Io+KEj4RtQ83Ele5geEY0v/z58f3OmjWLw4cP88tf/rL6cos1a9bw5ptv0rFjR66//vrqvnPnzuXgwYOMGzeOlJQUANavX89rr71GamoqN954Y3Xf559/ntzcXH7+85/Tvn17AL777jv+8Y9/kJiYyK233lrd9+9//zu7du1i5MiR1ddL7ty5k7/+9a/ExsZy++23V/edP38+W7du5cc//jE9evQAYN++fTz33HOEh4fXqDj92muvsXHjRq644gqysrIAyMvL4+mnn8blcnH33XdX933rrbdYs2YNw4cPZ+DAgQAUFhbyxBNPYLVauffee6v7vvvuu3z11VcMHTqU888/H/AvDfHoo48C1FhX7z//+Q+ff/455557LhdeeCHgTyiO/dtPnTq1OhlbunQpn3zyCQMGDOCSSy6p3sexvr/5zW/OyveIuFAHt06YztSZbqZWPUXs4c2Uzjkf+6i/s9uafsbvEacj6GO648ePr3PK3rGE6Zi0tLQ6Kz6d7j5FpGWrrPKwbe8BSsoqMBkGJpOByWQ6fr+67dh9E2aTgWEY1T9PbCstr6DgSCkFh4/dSk74/YT7R0ooKi7jdBevsNssxISH1Ei4YiP8v8ce/T0mIgSn/YRv5ao87Mw5yNa9B/yJ1J58tu45wN78gjqPGxsZQrvEGNolRdMpJZ5OKXEkx0c2SalcwzCICncTFe6mV6e21e1nUoo9kL766qvqb3y/+uqrOvu1xOtFROqrb4KV7pZ9rKlqwz2vfcNDb68np6gM8C8CGx9qY0TvtlzdK4nDu9azePHnRNmbdoFYkUBw2sx0axvHPVt+zSTTy/Ss2orn7yNw9J/MaZ/Ez0BQFxFurrTOlUjzUFnlYeuefDbsyGXjjlw2bM9ly558KquCuyZRqMtBZKiTiFAn4SFOnHYbBUdKyC8oJr/gCEXFZae9L7fTRkx4CIYBu/YX4PHUviZLRIiTdkn+JKpdUgztEmNIT4pullX8muM6V63BsdcmLy+P6Ojos27KT0ucFni6fRtzWuCJfcsrKrnpb1/x2Tb/4sVhDguXdI3nqh4JDOoQh/nolzL1+bdvzX8nmhbY8t8j3lq1Heu7k7ja5F9wOP+a1wnvfH693yNaxCLCzZmSK5GmV1nlYevefDZsP5pI7chly+7aE6lQl4PIMCderw+vz+f/6fXi9fnfEGu0+3y1th1jMZuICHESEeoiPMSfMEUeTZoiQl3+JCrERXiok8gQJ2Ehzh8cFSqvqCS/0J9oHUu48guOkHfC73kFR2q9NsrttB0diYohPfFoIpUUQ1RYy6jaBEquGoteGwmEgpIK5i3bTpc2YVzQORa7pXmVTxcJtOXf5bPspf8jjoMMvuMZ2sfWv8BFi1hEWETOXlVVHrbuPcCG7Tls3LGf9TtyTpFI2ck4er1PRlo8GakJtIk5s7VHfD4fvqOJmNlsCvi0MLvNSlJsBEmxEafsV1xWUZ14VVZ5SU+MIi4yVNPUAuTE6wV+yOuvv96IkYg0HxEuG5OGdQp2GCJNZlCHGOLveJgDxRUNSqzqS8mViDQqn8/H3vxC1m3LYd22HNZv28fGnfsprzh5ZDfUZa8unNAlzf8zMSY84MmGYRgYBphMwf3G1u2w4U6IIjVB1zU0hvDwpi99LSIizU+72BDaxTbNsZRciUhAHTpcwvrqRCqHddv3UXjk5GuQ3E4bGakJZKTGkZGWQOfUeJJiA59IydnrxRdfDHYIIiJyllFyJSINVlZeycaduSeMSuWwN7/wpH5Wi5mOybFkpifQJb0NmekJJMdFYjIpkRIREZHWQ8mViFTzen2UlldQUlZBcWkFxWUn3i+vvp9zsIj123LYuicfj/fkmjipCVFkpidUJ1Md2sZgs+rtRppWnz59WLJkCZGRkfTu3fuUo6KrVq1qwshERKS10qcdkbNEcWk5K9bt4Mt1OzlYVPy9xMl/v6Ssot77jQl3k9muTXUy1Tk1vlmWB5ezz9VXX43dbgdgxIgRwQ1GRETOCkquRFopn8/Hjn0HWbZmK8vWbOPrzXvqXEPp+8wmA5fDjttpw+Ww+QsvnHA/MsxFlzR/MhUbGdrIz0SkYe6///5a74uIiDQWJVcirUh5RSUrN+xi2ZqtLF+zjX35RTUeT4mPZGD3dFLiI3E77bgdRxMmp63GfZvVosIS0ip9+eWXrF+/HoDMzEyysrKCHJGIiLQmSq5EWrh9+YUs+8Y/OrVywy4qKo+XOLdZzPTunMyg7ukM7p5O2/jIIEYqEjy7d+9m1KhRfPrpp0RERABQUFDA4MGDmT9/Pm3btg1ugCIi0ioouRJpYSqrPHzz3R6Wr9nGsm+2sn3fwRqPx0eFHk2m2pHVJQWn3RqkSEWaj1tuuYXKykrWr19P586dAdi4cSPjxo3jlltuYfHixUGOUEREWgMlVyJnqLLKw+HiMopKyjhcUs7h4qM/S8o4XFyGD/8Iks1qwWa1YLceu39Cm6WWtqP3LWYTBwqL+WztNpat2caKb3fUKDxhNhl075DkT6h6tKNdYrSm9Il8z0cffcSyZcuqEyuAzp078+STT3LeeecFMTIREWlNlFyJ/ID8giN8tnY7a77bQ8GRUoq+lzyVVVT98E7OgGGA73vVziNDXQzqns6g7un0y0wlzK3qfCKnkpycTGVl5UntHo+HxMTEIEQkIiKtkZIrke+p8nj5dutelq/ZzvI1W9m8K++0tgtx2gl12wl1Ofy3o/fNJoOKSg/llVVUVFZRUenx/6zy/yw/se2Ex445llh1SUuoHp3KSI3XArwi9fDII48wYcIE5syZQ9++fQF/cYuJEyfy6KOPBjk6ERFpLZRciUD1tLvla7bxxbodHC4pr37MMCAjNYH+XVOJjwo9mjzZCXX7k6gwlx23y47ZZApYPF6vj0rP8YTLajFrdEqkniIjI2tMkS0uLmbAgAFYLP5TX1VVFRaLhZtuuknrYImISEAouZKzUpXHy7qt+1h+NKHatHN/jcfD3A4GdE1jYPd0BnRNIyrM1aTxmUwGdpMFu1X/i0rTsVtMWM0mPF4fXp8Pj9d/8/3wps3S7Nmzgx2CiIicZfTJTc4aBwuL+Wytf6rfiu+NTgFkpMUzqFs6A7unk5meENCRKJHmyADcdguhEVFEtM0kIqUr2EPBUwGeyqO3Cqoqy6mqrKCqopyqynI8VRV4KsvxVJXjqazEU1WBt6ocX2Ul3qpy3A57sJ8aAGPHjg12CCIicpZRciWtlsfrH51atmYbn63ZxsbvjU6FuhwM6JbKoO7tgjI6JRIMJgPCnVZCY9oSldoVZ5suEBp/ym0stK6TRVlZGRUVFTXawsLCghSNiIi0Jq3pfCnCwcJiPv92e3XJ8sMlZTUez0iNZ+DRKnsanZKzhc1sEO52EhqfTnRaNyxxncARHuywmlRxcTGTJ09m4cKFHDhw4KTHPR5PLVud2pw5c3jkkUfIycmhZ8+ePPnkk/Tv37/O/gUFBfz2t7/l9ddf5+DBg6SmpjJ79mwuu+yyeh9bRESaJyVX0uLlFxzh/c838J8vNrBhe26Nx0JdDvp3TfVP9+uWRlS4O0hRijQtl81MRGgIYUmdiUjOxIjuANaztyjKPffcw4cffsjTTz/NDTfcwJw5c9izZw9/+ctfePjhh+u9vwULFpCdnc3cuXMZMGAAs2fPZvjw4WzcuJG4uLiT+ldUVHDRRRcRFxfHa6+9RlJSEjt27CAiIiIAz05ERJoLJVfSIpWWV/LRqs0s/mwdX67bifeEhaA6p8T5R6e6pZPZrg0Ws0anpPUzGRDmsBIaEUVkSlfciRkQkQomc7BDaxb+9a9/8be//Y0LLriAcePGcd5559GhQwdSU1N5+eWXGT16dL32N2vWLG699VbGjRsHwNy5c3nnnXd44YUXmDJlykn9X3jhBQ4ePMiyZcuwWq0ApKWlnfHzEhGR5kXJlbQYHq+Xlet3sviz9Xy0ajOl5ccXBO3Wvg2XDMxkSJ+ORGt0Ss4SVrNBhMtGWEwSESmZ2OIzIDQh2GE1SwcPHqRdu3aA//qqgwcPAnDuuefyq1/9ql77qqioYOXKlUydOrW6zWQyMWzYMJYvX17rNm+99RaDBg3ijjvu4M033yQ2Npbrr7+eyZMnYzbXngCXl5dTXn688E5RUVG94hQRkaan5Eqave925bH4s3W8//kG8guOVLcnxUYwfGAXLhnYhbbxkUGMUKRpGIDLbibC7SSsTQfCj033c6gYww9p164d27ZtIyUlhYyMDBYuXEj//v3517/+Ve+pefn5+Xg8HuLjaxYCiY+PZ8OGDbVus3XrVv773/8yevRo3n33Xb777jtuv/12Kisruf/++2vdZsaMGTzwwAP1ik1ERIJLyZU0S3kFR/jg8/UsXr6e73bnVbeHuhwM69eZSwZ1oVv7xBoLhIq0RmaTQbjTSlh4BBHJXXDGd4aodDBbgx1aizJu3Di+/vprhgwZwpQpU7jyyit56qmnqKysZNasWY1+fK/XS1xcHM888wxms5msrCz27NnDI488UmdyNXXqVLKzs6t/LyoqIjk5udFjFRGRhlNyJc1GSVkFH331HYuXr2Pl+uPXUVnMJs7p0Y5LBmUyqHs6Ni2sK62cw2Iiwn10ul9yJubYjhDaBvRlQoPdeeed1feHDRvG+vXrWbVqFR06dKBHjx712ldMTAxms5nc3JoFdHJzc0lIqH1aZps2bbBarTWmAHbp0oWcnBwqKiqw2WwnbWO327Hbm8eaYSIicnr0KVUCamfOQQoOl+Jy2HA6rLgcNlx2KzarpdZRJo/Xy5frd7J4+To+/uq7GtdRdW+fyCWDMrmwbyfCQpxN+TREmoSBf2TKbDJwWM2Eux2EJ3YkJDEDNN2vUaWlpTW4oITNZiMrK4slS5YwYsQIwD8ytWTJEsaPH1/rNueccw6vvPIKXq8X09ElIDZt2kSbNm1qTaxERKRlCnpyVd91Ql599VWmTZvG9u3b6dixIzNnzqyxRsiRI0eYMmUKixYt4sCBA6Snp/PrX/+a2267rSmezllrzXd7mPf25yxfu63Wx80mA6fdhsthPfrTn3ztzDl00nVUlwzqwvCBmbSNi2ii6EUazmyAy27BYjKwHE2UTCYDs2FUJ04Wk4HJYsdkd2K2uTDbnFjsLix2t788usUJriiITNN0v0a0ZMkSHn/8cdavXw/4R44mTZrEsGHD6r2v7Oxsxo4dS9++fenfvz+zZ8+muLi4unrgmDFjSEpKYsaMGQD86le/4qmnnmLixIlMmDCBzZs384c//IFf//rXgXuCIiISdEFNruq7TsiyZcsYNWoUM2bM4IorruCVV15hxIgRrFq1im7dugH+E95///tfXnrpJdLS0nj//fe5/fbbSUxM5Kqrrmrqp9iq+Xw+Vm7Yybx3PmfVhl0AmAyDhJgwysorKSmroKyiCgCP18eR0nKOlJaftJ8wt4ML+3XmkkGZdGvXRtdRSbNmMiDEbiHMaSXMYSUkMg5zZArYXP4k6ViyZHWA1QUWB1idKokeZH/+85+ZOHEi11xzDRMnTgTgs88+47LLLuPxxx/njjvuqNf+Ro4cSV5eHtOnTycnJ4devXqxePHi6iIXO3furB6hAkhOTua9997jzjvvpEePHiQlJTFx4kQmT54cuCcpIiJBZ/h8JywQ1MQGDBhAv379eOqppwD/tIrk5GQmTJhQ6zohI0eOpLi4mLfffru6beDAgfTq1Yu5c+cC0K1bN0aOHMm0adOq+2RlZXHppZfy0EMPnVZcRUVFhIeHU1hYSFhYw6blLJ83BTxVDdq2ufP5fCz7Zivz3vmcb7fuA/zXRV06uCs3XNKvRuU+j9dbnWiVHP1ZWnbs9wpCnHb6ZaZiteiDpzRPBuC2W/xFJRwWQkJDsUSnQ2S6f6TJGRHkCFuXQLz/1qZt27ZMmTLlpGl7c+bM4Q9/+AN79uwJ2LEaS2O9NiIicmr1ef8N2shVQ9YJWb58eY3KSQDDhw9n0aJF1b8PHjyYt956i5tuuonExESWLl3Kpk2bePzxx+uMRWuJnB6v18fSVZv56zufsXmXv4KfzWrhqvO6M/qSvsRHnfzHZjaZcDvtuJ26KFtaBgNw2sz+ZMppJdRpxxqZ7K/QF5mmwhItVEFBAZdccslJ7RdffLFGj0REJGCCllw1ZJ2QnJycWvvn5ORU//7kk0/yi1/8grZt22KxWDCZTDz77LOcf/75dcaitUROrcrj5YMVG/jbO5+zI8e/8KbLbuXHQ3sx6qIsorRor7RwTquJMKf16OiUFWtYnD+RikyHiBSwqOBAS3fVVVfxxhtvcPfdd9dof/PNN7niiiuCFJWIiLQ2QS9oEWhPPvkkn332GW+99Rapqal8/PHH3HHHHSQmJtZ50bLWEqldcWk5//liI3//9wr25hUCEOqyc+2Fvbn2wj6Eq4KftCAGYLOYcFjNOCwmHDYzDosZt92M3RniT6aijk73U5W+VuFPf/pT9f3MzEx+//vfs3TpUgYNGgT4r7n69NNPueuuu4IVooiItDJBS64ask5IQkLCKfuXlpZy77338sYbb3D55ZcD0KNHD1avXs2jjz5aZ3J1tq8l4vF62b2/gC278/hudz7f7c5jy+489uUfnx4ZEerkuouy+OnQXpriJ81WjQTKeiyROn7fZDKBPRRc0eCM8v8MT4KQeE31a4W+Px08MjKSdevWsW7duuq2iIgIXnjhBe67776mDk9ERFqhoCVXDVknZNCgQSxZsoRJkyZVt33wwQfV30JWVlZSWVlZo0ITgNlsxuv1NsrzaGkKj5Ty3dEkyp9M5bF1zwEqKmsvvpEYG861F/bh6vO647CrRLQ0DxaTgdtuwWk1Ybeaj49GWc2YDAPMluPJU/Xt6O8qdX7W2Lat9qUhREREGktQpwXWd52QiRMnMmTIEB577DEuv/xy5s+fz5dffskzzzwDQFhYGEOGDOHuu+/G6XSSmprKRx99xN/+9jdmzZoVtOcZDFVVHnbkHmLL7jw278qrHpU6cU2pE9ltFtonxdC+bSwd2sbQoW0s7ZNitHivNCshdgvxYXaiQ+yYDeP4KNSxxOnYT3uYRqKkTseK5GrZBxERCbSgJlf1XSdk8ODBvPLKK9x3333ce++9dOzYkUWLFlWvcQUwf/58pk6dyujRozl48CCpqan8/ve/b7WLCPt8Pg4WlVRP5Ts2IrVt7wGqPLWP1iXGhNP+WALVNpaOybEkxoZj/t6In0hzYDYgOsROfJiDELsFIlOhbT+ISFWhCamXv/3tbzzyyCNs3rwZgE6dOnH33Xdzww03BDkyERFpLYK6zlVz1VzXuSqvrGL73gMnTesrOFxaa3+Xw0aHtsdGo/wjUu2SYnTNlLQITquJuDAHcaF2LBYrxGX6k6rQ+B/eWFqsxlrLadasWUybNo3x48dzzjnnAPDJJ58wZ84cHnroIe68886AHauxaJ0rEZHgaBHrXMmp+Xw+duQcZOWGXXyzeQ+bd+1nV+4hPN6Tc2HDgOT4yOqRqGPT+hKiwzTtRVoUA4h024gPsxPutGLY3JDYGxL7gD0k2OFJC/bkk0/y9NNPM2bMmOq2q666iq5du/J///d/LSK5EhGR5k/JVTOyN6+QlRt2snLDLlZt2El+YfFJfUJdDjomx9aY1tcuMVrFJqRFs5lNxIXZiQu1Y7eYwR3jH6WK7+YvTiFyhvbt28fgwYNPah88eDD79u0LQkQiItIa6VNLEOUdOszKDbtYuWEnqzbuqlH6HMBmMdOtQyJZnZPJSEugQ9sYYiJCNBolrUaYw0JCuINIl81/fWVkOiT3g6h2wQ5NWpkOHTqwcOFC7r333hrtCxYsoGPHjkGKSkREWhslV03o0OESvtq4qzqh2plzqMbjZrOJzLQEsrqkkJWRTLf2idit+ieS1sVsMogNsRMfZsdls/hHpuK7+Ueq3DHBDk9aqQceeICRI0fy8ccfV19z9emnn7JkyRIWLlwY5OhERKS10Cf3RnS4pIzVm3azaoM/ofpud16Nxw0DOqfGk9U5mawuKfTokITLoepn0jq5bGbiwxzEhNiwmEz+a6gS+/ivqbK5gh2etHI//elPWbFiBbNmzWLRokUAdOnShRUrVtC7d+/gBiciIq2GkqsAO3jwIH/84x95a+Hf2Lg9B+/3ijG2T4ohq0sKfTon06tTW8LcjiBFKtL4DCDKbSM+zEG48+h1gaEJ/lGquC5gMgc1Pjk7VFZW8stf/pJp06bx0ksvBTscERFpxZRcBZjD4WDWrFlUVlYCkBIfSVZGClldkundOZnIUH1DL62fzWwQF+ogLuxogQrDBDEdj65PlRzs8OQsY7Va+ec//8m0adOCHYqIiLRySq4CzOVy8cADD1C2bRlZnRKJjQwNdkgiTSbMYSE+zEGU24bJMMBihzY9ISkLnBHBDk/OYiNGjGDRokUquS4iIo1KyVUjmDp1aqMsIizSHJkNiA6xkxDuwG07+pbiioKkvpDQHSy6jlCCr2PHjjz44IN8+umnZGVl4Xa7azz+61//OkiRiYhIa6LkSkQaxGE1ER/mIC7U7i9QARCZ6p/6F93BX7FFpJl4/vnniYiIYOXKlaxcubLGY4ZhKLkSEZGAaFByVVZWhsNReyGGffv20aZNmzMKSkSaJ7PJIMplJSbETrjLioEBJgvEZ/qTqpC4YIcoUqtt27YFOwQRETkLmBqyUZ8+fVi9evVJ7f/85z/p0aPHmcYkIs2I2YBot43O8SH0TY2kQ1woES4bhj0U0s+DQbdDxuVKrKTF8Pl8+L5XyVVERCQQGpRcXXDBBQwcOJCZM2cCUFxczI033sgNN9zAvffeG9AARaTpmQx/CfWOcSFkpUbRKT6UKLfdX6QiIhm6XAkDb4e0c8Hm/uEdijQDzz//PN26dcPhcOBwOOjWrRvPPfdcsMMSEZFWpEHTAv/85z9z+eWXc8stt/D222+zb98+QkJCWLFiBd26dQt0jCLSBEwGhDutRIfYiXRZj19HBWAPhYRukNDDX6xCpIWZPn06s2bNYsKECQwaNAiA5cuXc+edd7Jz504efPDBIEcoIiKtQYMLWlx66aX85Cc/4emnn8ZisfCvf/1LiZVIC2MA4S4r0W4bUW5bzYTKZPYXpmjTEyLTwdSggW6RZuHpp5/m2WefZdSoUdVtV111FT169GDChAlKrkREJCAalFxt2bKF66+/npycHN577z0++ugjrrrqKiZOnMjvf/97rFZroOMUkQAxgDDn8YTKav5e0hQS50+o4jLBpkWvpXWorKykb9++J7VnZWVRVaVlM0REJDAa9FV0r169SEtL4+uvv+aiiy7ioYce4sMPP+T111+nf//+gY5RRAIkwmWlZ3IEmW3CiA9zHE+srA7/Qr99x0G/m6FtXyVW0qrccMMNPP300ye1P/PMM4wePToIEYmISGvU4GuubrjhhhptgwcPZvXq1UycODEggYlI4DisJlKj3US5TljQ1zD80/0SukNMJzBr2Ttp3Z5//nnef/99Bg4cCMDnn3/Ozp07GTNmDNnZ2dX9Zs2aFawQRUSkhWvQp6ndu3fzwgsvcNNNN9VoX7hwIZ06dQpIYCJy5swmg6QIJ23CHf5Kf+AvTpHY21+gwhEe3ABFmsjatWvp06cP4J/aDhATE0NMTAxr166t7mdo8WsRETkDDUqu/vKXv/DKK6+c1N61a1euu+46Jk+efMaBiciZiQmxkRLlwm4x+xsMk3+6X9p5YLGdemORVubDDz8MdggiInIWaFBylZOTQ5s2bU5qj42NZd++fWcclIg0nNtmJi3GTZjjhMIyYW2g06UQGh+8wERERERauQYlV8nJyXz66aekp6fXaP/0009JTEwMSGAiUj9Ws0FypIu4MDsGR6c2WWyQPsRfrELTnUREREQaVYOSq1tvvZVJkyZRWVnJj370IwCWLFnCPffcw1133RXQAEXk1AwgPsxBcpSz5jpVsZ2gw0XgCAtabCIiIiJnkwYlV3fffTcHDhzg9ttvp6KiAgCHw8HkyZOZOnVqQAMUkbqFOSykxbhx2074X9kRBh0vhpiOwQtMRERE5CzUoOTKMAxmzpzJtGnTWL9+PU6nk44dO2K32wMdn4jUwm4xkRrtItp9wv9zhsk//S/9fBWsEBEREQmCBi0ifExISAj9+vWjW7duSqxEmoDJgLaRTnomR9RMrEITIGssdBymxEqkDn//+98555xzSExMZMeOHQDMnj2bN998M8iRiYhIa3FGyVUgzJkzh7S0NBwOBwMGDGDFihWn7P/qq6+SkZGBw+Gge/fuvPvuuyf1Wb9+PVdddRXh4eG43W769evHzp07G+spiDSJKLeNnskRJEe6MB8rTmG2Qodh0GesP8ESkVo9/fTTZGdnc9lll1FQUIDH4wEgIiKC2bNnBzc4ERFpNYKaXC1YsIDs7Gzuv/9+Vq1aRc+ePRk+fDj79++vtf+yZcsYNWoUN998M1999RUjRoxgxIgRNRaA3LJlC+eeey4ZGRksXbqUb775hmnTpuFwOJrqaYkEhAG47WYSwuxktgmjc3wojmNrVoH/mqr+t0JyPzAF/XsSkWbtySef5Nlnn+W3v/0tZvPx/4/69u3LmjVrghiZiIi0JobP5/MF6+ADBgygX79+PPXUUwB4vV6Sk5OZMGECU6ZMOan/yJEjKS4u5u23365uGzhwIL169WLu3LkAXHfddVitVv7+9783OK6ioiLCw8MpLCwkLKxhldaWz5sCnqoGxyBnH7vFRIjdQojDQojdgttuOT5CVaNjKHS8CGI7N32QIo0sEO+/tXE6nWzYsIHU1FRCQ0P5+uuvadeuHZs3b6ZHjx6UlpYG7FiNpbFeGxERObX6vP8G7evuiooKVq5cybBhw44HYzIxbNgwli9fXus2y5cvr9EfYPjw4dX9vV4v77zzDp06dWL48OHExcUxYMAAFi1adMpYysvLKSoqqnETaUwmw1/pLzHcQaf4EPqkRNAnJZJO8aEkhjsJc1hPTqwMA9r29Y9WKbESqZf09HRWr159UvvixYvp0qVL0wckIiKtUoOqBQZCfn4+Ho+H+Pj4Gu3x8fFs2LCh1m1ycnJq7Z+TkwPA/v37OXLkCA8//DAPPfQQM2fOZPHixfzkJz/hww8/ZMiQIbXud8aMGTzwwAMBeFYitXNYTYTaLYQ4rITYLbhsZkz1WdQ3NB46XQphbRovSJFWLDs7mzvuuIOysjJ8Ph8rVqzgH//4BzNmzOC5554LdngiItJKBC25agxerxeAq6++mjvvvBOAXr16sWzZMubOnVtncjV16lSys7Orfy8qKiI5ObnxA5ZWLcRuIT7MTqTLhtVcz0FiqwMiUiAi1X9zx/hHrkSkQW655RacTif33XcfJSUlXH/99SQmJvLEE09w3XXXBTs8ERFpJYKWXMXExGA2m8nNza3RnpubS0JC7VXPEhISTtk/JiYGi8VCZmZmjT5dunThk08+qTMWu92uUvISECYDokPsJIQ5CLHX438viw3CUyAy1Z9UhcQrmRIJsNGjRzN69GhKSko4cuQIcXFxwQ5JRERamaAlVzabjaysLJYsWcKIESMA/8jTkiVLGD9+fK3bDBo0iCVLljBp0qTqtg8++IBBgwZV77Nfv35s3LixxnabNm0iNTW1UZ6HCIDDYiI+3EFcqB3L6VTuM1shPPmEZCpBFf9EmojL5cLlcgU7DBERaYWCOi0wOzubsWPH0rdvX/r378/s2bMpLi5m3LhxAIwZM4akpCRmzJgBwMSJExkyZAiPPfYYl19+OfPnz+fLL7/kmWeeqd7n3XffzciRIzn//PMZOnQoixcv5l//+hdLly4NxlOUVswAIlxW4sMcRLisGJxipMlsgbC2/kQqMhVC24DJXHd/EQmo3r17Y9QyGmwYBg6Hgw4dOnDjjTcydOjQIEQnIiKtRVCTq5EjR5KXl8f06dPJycmhV69eLF68uLpoxc6dOzGd8G3+4MGDeeWVV7jvvvu499576dixI4sWLaJbt27VfX784x8zd+5cZsyYwa9//Ws6d+7MP//5T84999wmf37SOllMBnGhduLDHDisp0iQTBZo0wNiMyAsyZ9giUhQXHLJJTz99NN0796d/v37A/DFF1/wzTffcOONN7Ju3TqGDRvG66+/ztVXXx3kaEVEpKUK6jpXzZXWuZLauGxm2oQ7iA6x177+1DFmKyT2huQBYA9pugBFWoHGWsvp1ltvJSUlhWnTptVof+ihh9ixYwfPPvss999/P++88w5ffvllwI4bSFrnSkQkOFrEOlciLYXJgORIJz2SwokLddSdWFnskHYODLoDOlyoxEqkGVm4cCGjRo06qf26665j4cKFAIwaNeqka3ZF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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(10, 4))\n", "plt.sca(ax[0])\n", "plt.fill_between(n_data, hl, hu, alpha=0.5, label=\"HESSE\")\n", "plt.fill_between(n_data, ml, mu, alpha=0.5, label=\"MINOS\")\n", "plt.plot(n_data, x, \"-k\")\n", "plt.legend()\n", "plt.xlabel(\"n\")\n", "plt.ylabel(\"cx\")\n", "plt.semilogx()\n", "plt.sca(ax[1])\n", "plt.plot(n_data, h_pcov, label=\"HESSE\")\n", "plt.plot(n_data, m_pcov, label=\"MINOS\")\n", "plt.axhline(0.68, ls=\":\", color=\"0.5\", zorder=0)\n", "plt.xlabel(r\"cx\")\n", "plt.ylabel(\"coverage probability\")\n", "plt.legend()\n", "plt.ylim(0, 1)\n", "plt.semilogx();" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)]" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/interactive.ipynb0000644000000000000000000003754114332717401016347 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Interactive fits\n", "\n", "This notebook showcases the interactive fitting capability of iminuit. Interactive fitting is useful to find good starting values and to debug the fit.\n", "\n", "**Note:** If you see this notebook on ReadTheDocs or otherwise statically rendered, changing the sliders won't change the plot. This requires a running Jupyter kernel." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from iminuit import cost\n", "from iminuit import Minuit\n", "from numba_stats import norm, t, bernstein, truncexpon\n", "import numpy as np\n", "from matplotlib import pyplot as plt" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## UnbinnedNLL" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "af38533713414893a5a3160dd562341a", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rng = np.random.default_rng(1)\n", "\n", "s = rng.normal(0.5, 0.1, size=1000)\n", "b = rng.exponential(1, size=1000)\n", "b = b[b < 1]\n", "x = np.append(s, b)\n", "\n", "truth = len(s) / len(x), 0.5, 0.1, 1.0\n", "\n", "n, xe = np.histogram(x, bins=50)\n", "\n", "def model(x, f, mu, sigma, slope):\n", " return f * norm.pdf(x, mu, sigma) + (1 - f) * truncexpon.pdf(x, 0, 1, 0, slope)\n", "\n", "c = cost.UnbinnedNLL(x, model)\n", "m = Minuit(c, *truth)\n", "m.limits[\"f\", \"mu\"] = (0, 1)\n", "m.limits[\"sigma\", \"slope\"] = (0, None)\n", "\n", "m.interactive(model_points=1000)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## ExtendedUnbinnedNLL" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "014dec0572b3499a8907f7e5aa017a13", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rng = np.random.default_rng(1)\n", "\n", "s = rng.normal(0.5, 0.1, size=1000)\n", "b = rng.exponential(1, size=1000)\n", "b = b[b < 1]\n", "x = np.append(s, b)\n", "\n", "truth = len(s), 0.5, 0.1, len(b), 1.0\n", "\n", "n, xe = np.histogram(x, bins=50)\n", "\n", "def model(x, s, mu, sigma, b, slope):\n", " x = s * norm.pdf(x, mu, sigma) + b * truncexpon.pdf(x, 0, 1, 0, slope)\n", " return s + b, x\n", "\n", "c = cost.ExtendedUnbinnedNLL(x, model)\n", "m = Minuit(c, *truth)\n", "m.limits[\"mu\"] = (0, 1)\n", "m.limits[\"sigma\", \"slope\", \"s\", \"b\"] = (0, None)\n", "\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## BinnedNLL" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c0f32bd8e6f44b51a3d0b4a95e465b03", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def model(xe, f, mu, sigma, nuinv, slope):\n", " nu = 1 / nuinv\n", " a, b = t.cdf((0, 1), nu, mu, sigma)\n", " sn = f * (t.cdf(xe, nu, mu, sigma) - a) / (b - a)\n", " bn = (1 - f) * truncexpon.cdf(xe, 0, 1, 0, slope)\n", " return sn + bn\n", "\n", "rng = np.random.default_rng(1)\n", "\n", "truth = 0.5, 0.5, 0.1, 0.1, 1\n", "\n", "xe = np.linspace(0, 1, 100)\n", "sm = truth[0] * np.diff(model(xe, 1, *truth[1:]))\n", "bm = (1 - truth[0]) * np.diff(model(xe, 0, *truth[1:]))\n", "n = rng.poisson(1000 * np.diff(model(xe, *truth)))\n", "\n", "c = cost.BinnedNLL(n, xe, model)\n", "\n", "m = Minuit(c, *truth)\n", "m.limits[\"sigma\", \"slope\"] = (0, None)\n", "m.limits[\"mu\", \"f\", \"nuinv\"] = (0, 1)\n", "\n", "m.interactive()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a69f7116f6464d81b70c64035439dd23", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = cost.BinnedNLL(n, xe, model)\n", "\n", "cx = 0.5 * (xe[1:] + xe[:-1])\n", "c.mask = np.abs(cx - 0.5) > 0.3\n", "\n", "m = Minuit(c, *truth)\n", "m.limits[\"sigma\", \"slope\"] = (0, None)\n", "m.limits[\"mu\", \"f\", \"nuinv\"] = (0, 1)\n", "\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## ExtendedBinnedNLL" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e26e22400b26486cab10b81550c8e5a3", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def model(xe, s, mu, sigma, nuinv, b1, b2, b3):\n", " nu = 1 / nuinv\n", " sn = s * t.cdf(xe, nu, mu, sigma)\n", " bn = bernstein.integral(xe, (b1, b2, b3), 0, 1)\n", " return sn + bn\n", "\n", "truth = 1000., 0.5, 0.1, 0.1, 1000., 3000., 2000.\n", "\n", "xe = np.linspace(0, 1, 100)\n", "\n", "rng = np.random.default_rng(1)\n", "n = rng.poisson(np.diff(model(xe, *truth)))\n", "\n", "c = cost.ExtendedBinnedNLL(n, xe, model)\n", "\n", "m = Minuit(c, *truth)\n", "m.limits[\"s\", \"sigma\", \"b1\", \"b2\", \"b3\"] = (0, None)\n", "m.limits[\"mu\", \"nuinv\"] = (0, 1)\n", "\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "You can pass a custom plotting routine with `Minuit.interactive` to draw more detail. A simple function works that accesses data from the outer scope, but we create a class in the following example to store the cost function, which has all data we need, because we override the variables in the outer scope in this notebook." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "f5c78e9b501f42bf8cecff95d73f2462", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Visualize signal and background components with different colors\n", "class Plotter:\n", " def __init__(self, cost):\n", " self.cost = cost\n", "\n", " def __call__(self, args):\n", " xe = self.cost.xe\n", " n = self.cost.data\n", " cx = 0.5 * (xe[1:] + xe[:-1])\n", " plt.errorbar(cx, n, n ** 0.5, fmt=\"ok\")\n", " sm = np.diff(self.cost.scaled_cdf(xe, *args[:4], 0, 0, 0))\n", " bm = np.diff(self.cost.scaled_cdf(xe, 0, *args[1:]))\n", " plt.stairs(bm, xe, fill=True, color=\"C1\")\n", " plt.stairs(bm + sm, xe, baseline = bm, fill=True, color=\"C0\")\n", "\n", "m.interactive(Plotter(c))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "7a127e1a231241f581d9ba86b67e6db2", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = cost.ExtendedBinnedNLL(n, xe, model)\n", "\n", "cx = 0.5 * (xe[1:] + xe[:-1])\n", "c.mask = np.abs(cx - 0.5) > 0.3\n", "\n", "m = Minuit(c, *truth)\n", "m.limits[\"s\", \"sigma\", \"nuinv\", \"b1\", \"b2\", \"b3\"] = (0, None)\n", "m.limits[\"mu\", \"nuinv\"] = (0, 1)\n", "m.fixed[\"s\", \"mu\", \"sigma\", \"nuinv\"] = True\n", "m.values[\"s\"] = 0\n", "\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## BarlowBeestonLite" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/tl/pv6mt7z17tz0stm1fjfg01cc0000gn/T/ipykernel_62711/3926893965.py:10: VisibleDeprecationWarning: BarlowBeestonLite was renamed to Template, please import Template instead\n", " c = cost.BarlowBeestonLite(n, xe, (b, s))\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3f292549fa924710b17a248500307a2b", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xe = np.linspace(0, 1, 20)\n", "bm = np.diff(truncexpon.cdf(xe, 0, 1, 0, 1))\n", "sm = np.diff(norm.cdf(xe, 0.5, 0.1))\n", "\n", "rng = np.random.default_rng(1)\n", "n = rng.poisson(1000 * bm + 100 * sm)\n", "b = rng.poisson(1e4 * bm)\n", "s = rng.poisson(1e2 * sm)\n", "\n", "c = cost.BarlowBeestonLite(n, xe, (b, s))\n", "\n", "m = Minuit(c, 1000, 100, name=(\"b\", \"s\"))\n", "m.limits = (0, None)\n", "\n", "m.interactive()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/tl/pv6mt7z17tz0stm1fjfg01cc0000gn/T/ipykernel_62711/3285115150.py:1: VisibleDeprecationWarning: BarlowBeestonLite was renamed to Template, please import Template instead\n", " c = cost.BarlowBeestonLite(n, xe, (b, s))\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "bc4860f11843424690f3d9e6fdd724d3", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = cost.BarlowBeestonLite(n, xe, (b, s))\n", "cx = 0.5 * (xe[1:] + xe[:-1])\n", "c.mask = np.abs(cx - 0.5) > 0.2\n", "\n", "m = Minuit(c, 1000, 100, name=(\"b\", \"s\"))\n", "m.limits = (0, None)\n", "\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## LeastSquares" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b8ae78e9220c435c96e715b55c8e2b13", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def model(x, a, b):\n", " return a + b * x\n", "\n", "truth = (1., 2.)\n", "x = np.linspace(0, 1)\n", "ym = model(x, *truth)\n", "ye = 0.1\n", "y = rng.normal(ym, ye)\n", "\n", "c = cost.LeastSquares(x, y, ye, model)\n", "\n", "m = Minuit(c, *truth)\n", "m.interactive()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "af47103035ca4c7cb50c31a9e5fb2831", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = cost.LeastSquares(x, y, ye, model)\n", "c.mask = (x > 0.6) | (x < 0.2)\n", "\n", "m = Minuit(c, *truth)\n", "m.interactive()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Cost functions with shared parameters" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "ea19670b589d4722b401f45f0b575bc3", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Output(), VBox(children=(HBox(children=(Button(description='Fit', style=ButtonStyle()), ToggleB…" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def model(xe, s, mu, sigma, nuinv):\n", " nu = 1 / nuinv \n", " return s * t.cdf(xe, nu, mu, sigma)\n", "\n", "truth = 100., 0.5, 0.1, 0.5\n", "\n", "rng = np.random.default_rng(1)\n", "xe = np.linspace(0, 1, 20)\n", "m = np.diff(model(xe, *truth))\n", "n = rng.poisson(m)\n", "\n", "c = cost.ExtendedBinnedNLL(n, xe, model) + cost.NormalConstraint([\"mu\", \"sigma\"], [0.5, 0.1], [0.1, 0.1])\n", "\n", "m = Minuit(c, *truth)\n", "\n", "m.interactive()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.9.16" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/memory_layout.ipynb0000644000000000000000000000724214332717401016732 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Performance impact of memory layout\n", "\n", "Modern CPUs are so fast that they are often wait for memory to be transferred. In case of memory access with a regular pattern, the CPU will prefetch memory that is likely to be used next. We can optimise this a little bit by arranging the numbers in memory that they are easy to fetch. For 1D data, there is not much that we can do, but for ND data, we have the choice between two layouts\n", "\n", "- x0, y0, ... x1, y1, ...\n", "- x0, x1, ..., y0, y1, ...\n", "\n", "Which one is more efficient is not obvious, so we try both options here. It turns out that the second option is better and that is the one used internally in the builtin cost functions as well." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import UnbinnedNLL\n", "import numpy as np\n", "from scipy.stats import multivariate_normal\n", "\n", "rng = np.random.default_rng(1)\n", "\n", "xy1 = rng.normal(size=(1_000_000, 2))\n", "xy2 = rng.normal(size=(2, 1_000_000))\n", "\n", "\n", "def cost1(x, y):\n", " return -np.sum(multivariate_normal.logpdf(xy1, (x, y)))\n", "\n", "cost1.errordef = Minuit.LIKELIHOOD\n", "\n", "def cost2(x, y):\n", " return -np.sum(multivariate_normal.logpdf(xy2.T, (x, y)))\n", "\n", "cost2.errordef = Minuit.LIKELIHOOD\n", "\n", "\n", "def logpdf(xy, x, y):\n", " return multivariate_normal.logpdf(xy.T, (x, y))\n", "\n", "cost3 = UnbinnedNLL(xy2, logpdf, log=True)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.68 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 1\n", "m = Minuit(cost1, x=0, y=0)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "470 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 1\n", "m = Minuit(cost2, x=0, y=0)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "528 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], "source": [ "%%timeit -n 1 -r 1\n", "m = Minuit(cost3, x=0, y=0)\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`cost2` and `cost3` are using the \"first all `x` then all `y`\" memory layout. `cost3` measures the small overhead incurred by using the built-in cost function `UnbinnedNLL` compared to a hand-tailored one." ] } ], "metadata": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" }, "kernelspec": { "display_name": "Python 3.8.12 ('venv': venv)", "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.10.8" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/numba.ipynb0000644000000000000000000011042214332717401015122 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Acceleration with Numba\n", "\n", "We explore how the computation of cost functions can be dramatically accelerated with numba's JIT compiler.\n", "\n", "The run-time of iminuit is usually dominated by the execution time of the cost function. To get good performance, it recommended to use array arthimetic and scipy and numpy functions in the body of the cost function. Python loops should be avoided, but if they are unavoidable, [Numba](https://numba.pydata.org/) can help. Numba can also parallelize numerical calculations to make full use of multi-core CPUs and even do computations on the GPU.\n", "\n", "Note: This tutorial shows how one can generate faster pdfs with Numba. Before you start to write your own pdf, please check whether one is already implemented in the [numba_stats library](https://github.com/HDembinski/numba-stats). If you have a pdf that is not included there, please consider contributing it to numba_stats." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "# !pip install matplotlib numpy numba scipy iminuit\n", "from iminuit import Minuit\n", "import numpy as np\n", "import numba as nb\n", "import math\n", "from scipy.stats import expon, norm\n", "from matplotlib import pyplot as plt\n", "from argparse import Namespace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The standard fit in particle physics is the fit of a peak over some smooth background. We generate a Gaussian peak over exponential background, using scipy." ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGwCAYAAACD0J42AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAxuklEQVR4nO3de1jVVb7H8c8GAVHZmzQBSVBsDMW8FaY7u1iSTKKTj5ZdmMK0fDJwVCZTy7yWmifTbEqny6hZZKfOo1Oampeyi6RoWl7StPRgKeCpZIsOyOV3/pjjPrPTKbZs2Ivt+/U8+3ncv9/i9/uuxolPa63f+tksy7IEAABgkCB/FwAAAPBLBBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOM08HcBF6KqqkpHjx5VRESEbDabv8sBAADVYFmWTp48qdjYWAUF/foYSb0MKEePHlVcXJy/ywAAABfgyJEjatmy5a+2qZcBJSIiQtI/O2i32/1cDQAAqA6Xy6W4uDj37/FfUy8DytlpHbvdTkABAKCeqc7yDBbJAgAA4xBQAACAcQgoAADAOPVyDQoAwDxVVVU6c+aMv8uAH4WEhCg4ONgn1yKgAABq7MyZMzp06JCqqqr8XQr8LDIyUjExMTXep4yAAgCoEcuydOzYMQUHBysuLu43N+BCYLIsS6dPn1ZRUZEkqUWLFjW6HgEFAFAjFRUVOn36tGJjY9WoUSN/lwM/Cg8PlyQVFRUpKiqqRtM9xFwAQI1UVlZKkkJDQ/1cCUxwNqSWl5fX6DoEFACAT/BuNEi++3tAQAEAAMYhoAAAAOOwSBYAUCtaj19Vp/c7PCvNq/a9evVSly5dNG/evFqpZ8iQITpx4oRWrFhRK9f3h8OHDyshIUE7duxQly5davVejKAAAADjEFAAAKgnLqadegkoAICLVkVFhbKysuRwOHTppZfqiSeekGVZkqSlS5cqOTlZERERiomJ0T333OPehOysPXv2qF+/frLb7YqIiND111+vb7/99rz3ysvLU/PmzfX000+7jz355JOKiopSRESEHnjgAY0fP95j6mTIkCEaMGCAnnrqKcXGxioxMVGStGvXLt18880KDw9Xs2bNNHz4cJWUlLh/rlevXho9erTH/QcMGKAhQ4a4v7du3VozZszQ0KFDFRERofj4eL300kseP7N161Z17dpVDRs2VHJysnbs2FHtf7Y1xRqU86jOvKm3c50AAPMsWbJEw4YN09atW7Vt2zYNHz5c8fHxevDBB1VeXq7p06crMTFRRUVFys7O1pAhQ/T+++9Lkn744QfdcMMN6tWrlzZu3Ci73a7PPvtMFRUV59xn48aNGjhwoGbPnq3hw4dLkt544w099dRTevHFF9WzZ08tW7ZMc+bMUUJCgsfPbtiwQXa7XevWrZMknTp1SqmpqXI6ncrLy1NRUZEeeOABZWVlafHixV71f86cOZo+fboee+wxvfPOOxoxYoRuvPFGJSYmqqSkRP369dMtt9yi119/XYcOHdKoUaMu4J/yhSGgAAAuWnFxcZo7d65sNpsSExO1a9cuzZ07Vw8++KCGDh3qbtemTRvNnz9f3bp1U0lJiZo0aaIXXnhBDodDy5YtU0hIiCTpiiuuOOcey5cv13333adXXnlFd955p/v4888/r2HDhun++++XJE2aNEkffPCBx0iIJDVu3FivvPKKeyO8l19+WaWlpXrttdfUuHFjSdJf/vIX9e/fX08//bSio6Or3f++ffvq4YcfliSNGzdOc+fO1YcffqjExETl5OSoqqpKr776qho2bKgOHTro+++/14gRI6p9/ZpgigcAcNHq0aOHx8ZiTqdTBw4cUGVlpbZv367+/fsrPj5eERERuvHGGyVJ+fn5kqSdO3fq+uuvd4eT89myZYvuuOMOLV261COcSNL+/ft1zTXXeBz75XdJ6tixo8cuvV9//bU6d+7sDieS1LNnT1VVVWn//v1e9F7q1KmT+882m00xMTHuaayvv/5anTp1UsOGDd1tnE6nV9evCQIKAAC/UFpaqtTUVNntdr3xxhvKy8vT8uXLJf3/QtWz7535NZdffrnatWunv/3tbxe89fu/BpHqCgoKcq+lOet89/9luLLZbMa8kZqAAgC4aG3ZssXj++eff662bdtq3759+vHHHzVr1ixdf/31ateu3TkLZDt16qRPPvnkV4PHpZdeqo0bN+rgwYMaPHiwR9vExETl5eV5tP/l9/Np3769vvzyS506dcp97LPPPlNQUJB7EW3z5s117Ngx9/nKykrt3r37N6/9y/t89dVXKi0tdR/7/PPPvbpGTRBQAAAXrfz8fGVnZ2v//v1688039fzzz2vUqFGKj49XaGionn/+eX333Xd69913NX36dI+fzcrKksvl0l133aVt27bpwIEDWrp06TnTLFFRUdq4caP27dunu+++272IduTIkXr11Ve1ZMkSHThwQE8++aS++uqr33yXTXp6uho2bKiMjAzt3r1bH374oUaOHKl7773Xvf7k5ptv1qpVq7Rq1Srt27dPI0aM0IkTJ7z6Z3PPPffIZrPpwQcf1N69e/X+++/rmWee8eoaNcEiWQBAragPTzved999+sc//qFrrrlGwcHBGjVqlIYPHy6bzabFixfrscce0/z583XVVVfpmWee0R/+8Af3zzZr1kwbN27U2LFjdeONNyo4OFhdunRRz549z7lPTEyMNm7cqF69eik9PV05OTlKT0/Xd999p0ceeUSlpaUaPHiwhgwZoq1bt/5qzY0aNdLatWs1atQodevWTY0aNdKgQYP07LPPutsMHTpUX375pe677z41aNBAY8aM0U033eTVP5smTZrovffe00MPPaSuXbsqKSlJTz/9tAYNGuTVdS6UzfrlJFU94HK55HA4VFxcLLvd7vPr85gxAFRfaWmpDh06pISEBI8FlfDeLbfcopiYGC1dutTfpVywX/v74M3vb0ZQAADwg9OnT2vhwoVKTU1VcHCw3nzzTa1fv96938nFjoACAIAf2Gw2vf/++3rqqadUWlqqxMRE/dd//ZdSUlL8XZoRCCgAAPhBeHi41q9f7+8yjMVTPAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQDgF4YMGaIBAwbU+X2nTJmiLl261Pl9TcRjxgCA2jHFUcf3K/bZpZ577rlz3gaMukVAAQDgFxyOOg5XOAdTPACAi9Y777yjjh07Kjw8XM2aNVNKSopOnTp1zhTPyZMnlZ6ersaNG6tFixaaO3euevXqpdGjR7vbtG7dWjNmzNDQoUMVERGh+Ph4vfTSSx73GzdunK644go1atRIbdq00RNPPKHy8vI66m39QkABAFyUjh07prvvvltDhw7V119/rY8++kgDBw4879ROdna2PvvsM7377rtat26dPvnkE33xxRfntJszZ46Sk5O1Y8cOPfzwwxoxYoT279/vPh8REaHFixdr7969eu655/Tyyy9r7ty5tdrP+oopHgDARenYsWOqqKjQwIED1apVK0lSx44dz2l38uRJLVmyRDk5Oerdu7ckadGiRYqNjT2nbd++ffXwww9L+udoydy5c/Xhhx8qMTFRkjRx4kR329atW+uRRx7RsmXL9Oijj/q8f/UdAQUAcFHq3LmzevfurY4dOyo1NVV9+vTR7bffrksuucSj3Xfffafy8nJdc8017mMOh8MdOv5Vp06d3H+22WyKiYlRUVGR+9hbb72l+fPn69tvv1VJSYkqKipkt9troXf1n9dTPD/88IP++Mc/qlmzZgoPD1fHjh21bds293nLsjRp0iS1aNFC4eHhSklJ0YEDBzyu8dNPPyk9PV12u12RkZEaNmyYSkpKat4bAACqKTg4WOvWrdPq1auVlJSk559/XomJiTp06NAFXzMkJMTju81mU1VVlSQpNzdX6enp6tu3r1auXKkdO3bo8ccf15kzZ2rUj0DlVUD5+eef1bNnT4WEhGj16tXau3ev5syZ45E2Z8+erfnz52vhwoXasmWLGjdurNTUVJWWlrrbpKena8+ePVq3bp1Wrlypjz/+WMOHD/ddrwAAqAabzaaePXtq6tSp2rFjh0JDQ7V8+XKPNm3atFFISIjy8vLcx4qLi/XNN994da/NmzerVatWevzxx5WcnKy2bdvqv//7v33Sj0Dk1RTP008/rbi4OC1atMh9LCEhwf1ny7I0b948TZw4Ubfddpsk6bXXXlN0dLRWrFihu+66S19//bXWrFmjvLw8JScnS5Kef/559e3bV88888x55/TKyspUVlbm/u5yubzrJQAAv7BlyxZt2LBBffr0UVRUlLZs2aLjx4+rffv2+uqrr9ztIiIilJGRobFjx6pp06aKiorS5MmTFRQUJJvNVu37tW3bVvn5+Vq2bJm6deumVatWnROG8P+8GkF59913lZycrDvuuENRUVHq2rWrXn75Zff5Q4cOqaCgQCkpKe5jDodD3bt3V25urqR/DnFFRka6w4kkpaSkKCgoSFu2bDnvfWfOnCmHw+H+xMXFedVJAAB+yW636+OPP1bfvn11xRVXaOLEiZozZ45uvfXWc9o+++yzcjqd6tevn1JSUtSzZ0+1b99eDRs2rPb9/vCHP2jMmDHKyspSly5dtHnzZj3xxBO+7FJAsVlebJV39n+I7Oxs3XHHHcrLy9OoUaO0cOFCZWRkaPPmzerZs6eOHj2qFi1auH9u8ODBstlseuuttzRjxgwtWbLE47ErSYqKitLUqVM1YsSIc+57vhGUuLg4FRcX18ri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Prdp0yZlZmaqW7duqqio0GOPPaY+ffpo7969aty4sb/Lq7GWLVtq1qxZatu2rSzL0pIlS3Tbbbdpx44d6tChg7/L86m8vDz99a9/VadOnfxdis916NBB69evd39v0CBw/hX1888/q2fPnrrpppu0evVqNW/eXAcOHNAll1zi79J8Ji8vT5WVle7vu3fv1i233KI77rjDj1X5ztNPP60FCxZoyZIl6tChg7Zt26b7779fDodDf/rTn/xdnk888MAD2r17t5YuXarY2Fi9/vrrSklJ0d69e3XZZZfVbTEWziHJWr58ub/LqFVFRUWWJGvTpk3+LqXWXHLJJdYrr7zi7zJ86uTJk1bbtm2tdevWWTfeeKM1atQof5fkM5MnT7Y6d+7s7zJqzbhx46zrrrvO32XUqVGjRlmXX365VVVV5e9SfCItLc0aOnSox7GBAwda6enpfqrIt06fPm0FBwdbK1eu9Dh+1VVXWY8//nid18MUz0WquLhYktS0aVM/V+J7lZWVWrZsmU6dOhVwr07IzMxUWlqaUlJS/F1KrThw4IBiY2PVpk0bpaenKz8/398l+cy7776r5ORk3XHHHYqKilLXrl318ssv+7usWnPmzBm9/vrrGjp0qM9f6uov1157rTZs2KBvvvlGkvTll1/q008/1a233urnynyjoqJClZWVatiwocfx8PBwffrpp3VeT+CMn6LaqqqqNHr0aPXs2VNXXnmlv8vxmV27dsnpdKq0tFRNmjTR8uXLlZSU5O+yfGbZsmX64osv/DYfXNu6d++uxYsXKzExUceOHdPUqVN1/fXXa/fu3YqIiPB3eTX23XffacGCBcrOztZjjz2mvLw8/elPf1JoaKgyMjL8XZ7PrVixQidOnNCQIUP8XYrPjB8/Xi6XS+3atVNwcLAqKyv11FNPKT093d+l+URERIScTqemT5+u9u3bKzo6Wm+++aZyc3P1u9/9ru4LqvMxm3pAAT7F89BDD1mtWrWyjhw54u9SfKqsrMw6cOCAtW3bNmv8+PHWpZdeau3Zs8ffZflEfn6+FRUVZX355ZfuY4E2xfNLP//8s2W32wNmmi4kJMRyOp0ex0aOHGn16NHDTxXVrj59+lj9+vXzdxk+9eabb1otW7a03nzzTeurr76yXnvtNatp06bW4sWL/V2azxw8eNC64YYbLElWcHCw1a1bNys9Pd1q165dnddCQDmPQA4omZmZVsuWLa3vvvvO36XUut69e1vDhw/3dxk+sXz5cve/MM5+JFk2m80KDg62Kioq/F1irUhOTrbGjx/v7zJ8Ij4+3ho2bJjHsRdffNGKjY31U0W15/Dhw1ZQUJC1YsUKf5fiUy1btrT+8pe/eBybPn26lZiY6KeKak9JSYl19OhRy7Isa/DgwVbfvn3rvAbWoFwkLMtSVlaWli9fro0bNyohIcHfJdW6qqoqlZWV+bsMn+jdu7d27dqlnTt3uj/JyclKT0/Xzp07FRwc7O8Sfa6kpETffvutWrRo4e9SfKJnz57nPNr/zTffqFWrVn6qqPYsWrRIUVFRSktL83cpPnX69GkFBXn+2gwODlZVVZWfKqo9jRs3VosWLfTzzz9r7dq1uu222+q8Btag/J+SkhIdPHjQ/f3QoUPauXOnmjZtqvj4eD9W5huZmZnKycnR3//+d0VERKigoECS5HA4FB4e7ufqam7ChAm69dZbFR8fr5MnTyonJ0cfffSR1q5d6+/SfCIiIuKc9UKNGzdWs2bNAmYd0SOPPKL+/furVatWOnr0qCZPnqzg4GDdfffd/i7NJ8aMGaNrr71WM2bM0ODBg7V161a99NJLeumll/xdmk9VVVVp0aJFysjICKjHxCWpf//+euqppxQfH68OHTpox44devbZZzV06FB/l+Yza9eulWVZSkxM1MGDBzV27Fi1a9dO999/f90XU+djNob68MMPLUnnfDIyMvxdmk+cr2+SrEWLFvm7NJ8YOnSo1apVKys0NNRq3ry51bt3b+uDDz7wd1m1KtDWoNx5551WixYtrNDQUOuyyy6z7rzzTuvgwYP+Lsun3nvvPevKK6+0wsLCrHbt2lkvvfSSv0vyubVr11qSrP379/u7FJ9zuVzWqFGjrPj4eKthw4ZWmzZtrMcff9wqKyvzd2k+89Zbb1lt2rSxQkNDrZiYGCszM9M6ceKEX2qxWVYAbYEHAAACAmtQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAvzt+/LhiYmI0Y8YM97HNmzcrNDRUGzZs8GNlAPyFlwUCMML777+vAQMGaPPmzUpMTFSXLl1022236dlnn/V3aQD8gIACwBiZmZlav369kpOTtWvXLuXl5SksLMzfZQHwAwIKAGP84x//0JVXXqkjR45o+/bt6tixo79LAuAnrEEBYIxvv/1WR48eVVVVlQ4fPuzvcgD4ESMoAIxw5swZXXPNNerSpYsSExM1b9487dq1S1FRUf4uDYAfEFAAGGHs2LF655139OWXX6pJkya68cYb5XA4tHLlSn+XBsAPmOIB4HcfffSR5s2bp6VLl8putysoKEhLly7VJ598ogULFvi7PAB+wAgKAAAwDiMoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADDO/wJXmefOucAL1AAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "np.random.seed(1) # fix seed\n", "\n", "# true parameters for signal and background\n", "truth = Namespace(n_sig=2000, f_bkg=10, sig=(5.0, 0.5), bkg=(0.0, 4.0))\n", "n_bkg = truth.n_sig * truth.f_bkg\n", "\n", "# make a data set\n", "x = np.empty(truth.n_sig + n_bkg)\n", "\n", "# fill m variables\n", "x[: truth.n_sig] = norm(*truth.sig).rvs(truth.n_sig)\n", "x[truth.n_sig :] = expon(*truth.bkg).rvs(n_bkg)\n", "\n", "# cut a range in x\n", "xrange = np.array((1.0, 9.0))\n", "ma = (xrange[0] < x) & (x < xrange[1])\n", "x = x[ma]\n", "\n", "plt.hist(\n", " (x[truth.n_sig :], x[: truth.n_sig]),\n", " bins=50,\n", " stacked=True,\n", " label=(\"background\", \"signal\"),\n", ")\n", "plt.xlabel(\"x\")\n", "plt.legend();" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [], "source": [ "# ideal starting values for iminuit\n", "start = np.array((truth.n_sig, n_bkg, truth.sig[0], truth.sig[1], truth.bkg[1]))\n", "\n", "\n", "# iminuit instance factory, will be called a lot in the benchmarks blow\n", "def m_init(fcn):\n", " m = Minuit(fcn, start, name=(\"ns\", \"nb\", \"mu\", \"sigma\", \"lambd\"))\n", " m.limits = ((0, None), (0, None), None, (0, None), (0, None))\n", " m.errordef = Minuit.LIKELIHOOD\n", " return m" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-103168.78482586428" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# extended likelihood (https://doi.org/10.1016/0168-9002(90)91334-8)\n", "# this version uses numpy and scipy and array arithmetic\n", "def nll(par):\n", " n_sig, n_bkg, mu, sigma, lambd = par\n", " s = norm(mu, sigma)\n", " b = expon(0, lambd)\n", " # normalisation factors are needed for pdfs, since x range is restricted\n", " sn = s.cdf(xrange)\n", " bn = b.cdf(xrange)\n", " sn = sn[1] - sn[0]\n", " bn = bn[1] - bn[0]\n", " return (n_sig + n_bkg) - np.sum(\n", " np.log(s.pdf(x) / sn * n_sig + b.pdf(x) / bn * n_bkg)\n", " )\n", "\n", "\n", "nll(start)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "132 ms ± 2.15 ms per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -r 3 -n 1\n", "m = m_init(nll) # setup time is negligible\n", "m.migrad();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see whether we can beat that. The code above is already pretty fast, because numpy and scipy routines are fast, and we spend most of the time in those. But these implementations do not parallelize the execution and are not optimised for this particular CPU, unlike numba-jitted functions.\n", "\n", "To use numba, in theory we just need to put the `njit` decorator on top of the function, but often that doesn't work out of the box. numba understands many numpy functions, but no scipy. We must evaluate the code that uses scipy in 'object mode', which is numba-speak for calling into the Python interpreter." ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-103168.78482586429" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# first attempt to use numba\n", "@nb.njit(parallel=True)\n", "def nll(par):\n", " n_sig, n_bkg, mu, sigma, lambd = par\n", " with nb.objmode(spdf=\"float64[:]\", bpdf=\"float64[:]\", sn=\"float64\", bn=\"float64\"):\n", " s = norm(mu, sigma)\n", " b = expon(0, lambd)\n", " # normalisation factors are needed for pdfs, since x range is restricted\n", " sn = np.diff(s.cdf(xrange))[0]\n", " bn = np.diff(b.cdf(xrange))[0]\n", " spdf = s.pdf(x)\n", " bpdf = b.pdf(x)\n", " no = n_sig + n_bkg\n", " return no - np.sum(np.log(spdf / sn * n_sig + bpdf / bn * n_bkg))\n", "\n", "\n", "nll(start) # test and warm-up JIT" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "157 ms ± 3.75 ms per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -r 3 -n 1 m = m_init(nll)\n", "m.migrad()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is even a bit slower. :( Let's break the original function down by parts to see why." ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "475 µs ± 3.24 µs per loop (mean ± std. dev. of 3 runs, 100 loops each)\n", "451 µs ± 5.18 µs per loop (mean ± std. dev. of 3 runs, 500 loops each)\n", "66.3 µs ± 219 ns per loop (mean ± std. dev. of 3 runs, 1,000 loops each)\n" ] } ], "source": [ "# let's time the body of the function\n", "n_sig, n_bkg, mu, sigma, lambd = start\n", "s = norm(mu, sigma)\n", "b = expon(0, lambd)\n", "# normalisation factors are needed for pdfs, since x range is restricted\n", "sn = np.diff(s.cdf(xrange))[0]\n", "bn = np.diff(b.cdf(xrange))[0]\n", "spdf = s.pdf(x)\n", "bpdf = b.pdf(x)\n", "\n", "%timeit -r 3 -n 100 norm(*start[2:4]).pdf(x)\n", "%timeit -r 3 -n 500 expon(0, start[4]).pdf(x)\n", "%timeit -r 3 -n 1000 n_sig + n_bkg - np.sum(np.log(spdf / sn * n_sig + bpdf / bn * n_bkg))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Most of the time is spend in `norm` and `expon` which numba could not accelerate and the total time is dominated by the slowest part.\n", "\n", "This, unfortunately, means we have to do much more manual work to make the function faster, since we have to replace the scipy routines with Python code that numba can accelerate and run in parallel." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-103168.78482586429" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# when parallel is enabled, also enable associative math\n", "kwd = {\"parallel\": True, \"fastmath\": {\"reassoc\", \"contract\", \"arcp\"}}\n", "\n", "\n", "@nb.njit(**kwd)\n", "def sum_log(fs, spdf, fb, bpdf):\n", " return np.sum(np.log(fs * spdf + fb * bpdf))\n", "\n", "\n", "@nb.njit(**kwd)\n", "def norm_pdf(x, mu, sigma):\n", " invs = 1.0 / sigma\n", " z = (x - mu) * invs\n", " invnorm = 1 / np.sqrt(2 * np.pi) * invs\n", " return np.exp(-0.5 * z ** 2) * invnorm\n", "\n", "\n", "@nb.njit(**kwd)\n", "def nb_erf(x):\n", " y = np.empty_like(x)\n", " for i in nb.prange(len(x)):\n", " y[i] = math.erf(x[i])\n", " return y\n", "\n", "\n", "@nb.njit(**kwd)\n", "def norm_cdf(x, mu, sigma):\n", " invs = 1.0 / (sigma * np.sqrt(2))\n", " z = (x - mu) * invs\n", " return 0.5 * (1 + nb_erf(z))\n", "\n", "\n", "@nb.njit(**kwd)\n", "def expon_pdf(x, lambd):\n", " inv_lambd = 1.0 / lambd\n", " return inv_lambd * np.exp(-inv_lambd * x)\n", "\n", "\n", "@nb.njit(**kwd)\n", "def expon_cdf(x, lambd):\n", " inv_lambd = 1.0 / lambd\n", " return 1.0 - np.exp(-inv_lambd * x)\n", "\n", "\n", "def nll(par):\n", " n_sig, n_bkg, mu, sigma, lambd = par\n", " # normalisation factors are needed for pdfs, since x range is restricted\n", " sn = norm_cdf(xrange, mu, sigma)\n", " bn = expon_cdf(xrange, lambd)\n", " sn = sn[1] - sn[0]\n", " bn = bn[1] - bn[0]\n", " spdf = norm_pdf(x, mu, sigma)\n", " bpdf = expon_pdf(x, lambd)\n", " no = n_sig + n_bkg\n", " return no - sum_log(n_sig / sn, spdf, n_bkg / bn, bpdf)\n", "\n", "\n", "nll(start) # test and warm-up JIT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see how well these versions do:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "111 µs ± 18.5 µs per loop (mean ± std. dev. of 5 runs, 100 loops each)\n", "56.9 µs ± 1.02 µs per loop (mean ± std. dev. of 5 runs, 500 loops each)\n", "59.1 µs ± 929 ns per loop (mean ± std. dev. of 5 runs, 1,000 loops each)\n" ] } ], "source": [ "%timeit -r 5 -n 100 norm_pdf(x, *start[2:4])\n", "%timeit -r 5 -n 500 expon_pdf(x, start[4])\n", "%timeit -r 5 -n 1000 sum_log(n_sig / sn, spdf, n_bkg / bn, bpdf)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Only a minor improvement for `sum_log`, but the pdf calculation was drastically accelerated. Since this was the bottleneck before, we expect also Migrad to finish faster now." ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "42.7 ms ± 1.59 ms per loop (mean ± std. dev. of 3 runs, 1 loop each)\n" ] } ], "source": [ "%%timeit -r 3 -n 1\n", "m = m_init(nll) # setup time is negligible\n", "m.migrad();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Success! We managed to get a big speed improvement over the initial code. This is impressive, but it cost us a lot of developer time. This is not always a good trade-off, especially if you consider that library routines are heavily tested, while you always need to test your own code in addition to writing it.\n", "\n", "By putting these faster functions into a library, however, we would only have to pay the developer cost once. You can find those in the [numba-stats](https://github.com/HDembinski/numba-stats) library." ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "63.1 µs ± 4.25 µs per loop (mean ± std. dev. of 5 runs, 100 loops each)\n", "63.9 µs ± 1.08 µs per loop (mean ± std. dev. of 5 runs, 500 loops each)\n" ] } ], "source": [ "from numba_stats import norm, expon\n", "\n", "%timeit -r 5 -n 100 norm.pdf(x, *start[2:4])\n", "%timeit -r 5 -n 500 expon.pdf(x, 0, start[4])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The implementation of the normal pdf in numba-stats is even faster than our simple implementation here.\n", "\n", "Try to compile the functions again with `parallel=False` to see how much of the speed increase came from the parallelization and how much from the generally optimized code that `numba` generated for our specific CPU. On my machine, the gain was entirely due to numba.\n", "\n", "In general, it is good advice to not automatically add `parallel=True`, because this comes with an overhead of breaking data into chunks, copy chunks to the individual CPUs and finally merging everything back together. For large arrays, this overhead is negligible, but for small arrays, it can be a net loss.\n", "\n", "So why is `numba` so fast even without parallelization? We can look at the assembly code generated." ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "signature: (array(float64, 1d, C), float64, float64)\n", "----------------------------------------------------\n", "\t.section\t__TEXT,__text,regular,pure_instructions\n", "\t.build_version macos, 12, 0\n", "\t.globl\t__ZN8__main__8norm_pdfB3v76B146c8tJTC_2fWQAlbW1yBC0oR6GELEUMELYSPGrIQMVjAQniQcIXKQIMVwoOGKoQDDVQQR1NHAS2FQ9XgSs8w86AhbIsexNXqiUXJBeo6CupFwBRdnJ8MYibn55UUJSaXqNcC7QPGIsRqAA_3d_3dE5ArrayIdLi1E1C7mutable7alignedEdd\n", "\t.p2align\t2\n", "__ZN8__main__8norm_pdfB3v76B146c8tJTC_2fWQAlbW1yBC0oR6GELEUMELYSPGrIQMVjAQniQcIXKQIMVwoOGKoQDDVQQR1NHAS2FQ9XgSs8w86AhbIsexNXqiUXJBeo6CupFwBRdnJ8MYibn55UUJSaXqNcC7QPGIsRqAA_3d_3dE5ArrayIdLi1E1C7mutable7alignedEdd:\n", "\t.cfi_startproc\n", "\tstp\td9, d8, [sp, #-112]!\n", "\tstp\tx28, x27, [sp, #16]\n", "\tstp\tx26, x25, [sp, #32]\n", "\tstp\tx24, x23, [sp, #48]\n", "\tstp\tx22, x21, [sp, #64]\n", "\tstp\tx20, x19, [sp, #80]\n", "\tstp\tx29, x30, [sp, #96]\n", "\tadd\tx29, sp, #96\n", "\tsub\tsp, sp, #368\n", "\tmov\tx19, sp\n", "\t.cfi_def_cfa w29, 16\n", "\t.cfi_offset w30, -8\n", "\t.cfi_offset w29, -16\n", "\t.cfi_offset w19, -24\n", "\t.cfi_offset w20, -32\n", "\t.cfi_offset w21, -40\n", "\t.cfi_offset w22, -48\n", "\t.cfi_offset w23, -56\n", "\t.cfi_offset w24, -64\n", "\t.cfi_offset w25, -72\n", "\t.cfi_offset w26\n", "[...]\n" ] } ], "source": [ "for signature, code in norm_pdf.inspect_asm().items():\n", " print(f\"signature: {signature}\\n{'-'*(len(str(signature)) + 11)}\\n{code[:1000]}\\n[...]\")\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This code section is very long, but the assembly grammar is very simple. Constants start with `.` and `SOMETHING:` is a jump label for the assembly equivalent of `goto`. Everything else is an instruction with its name on the left and the arguments are on the right.\n", "\n", "The SIMD instructions are the interesting commands that operate on multiple values at once. This is where the speed comes from. \n", "- If you are on the **x86** platform, those instructions end with `pd` and `ps`.\n", "- On **arch64**, they contain a dot `.` and some letters/numbers afterwards." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "signature: (array(float64, 1d, C), float64, float64)\n", "----------------------------------------------------\n", "Instructions\n", "add : 44\n", "adds : 2\n", "fmov : 3\n", "fmov.2d : 1\n", "fmul : 5\n", "fmul.2d : 5\n", "fsub : 1\n", "fsub.2d : 2\n", "madd : 6\n", "mov : 108\n", "mov.16b : 6\n", "mov.d : 1\n", "movi.16b : 5\n", "movk : 3\n", "mul : 3\n", "smulh : 1\n", "sub : 23\n", "subs : 1\n" ] } ], "source": [ "import re\n", "from collections import Counter\n", "\n", "for signature, code in norm_pdf.inspect_asm().items():\n", " print(f\"signature: {signature}\\n{'-'*(len(str(signature)) + 11)}\") \n", " instructions = []\n", " for line in code.split(\"\\n\"):\n", " instr = line.strip().split(\"\\t\")[0]\n", " if instr.startswith(\".\"): continue\n", " for match in (\"add\", \"sub\", \"mul\", \"mov\"):\n", " if match in instr:\n", " instructions.append(instr)\n", " c = Counter(instructions)\n", " print(\"Instructions\")\n", " for k in sorted(c):\n", " print(f\"{k:10}: {c[k]:5}\")\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "- `add`: subtract numbers\n", "- `sub`: subtract numbers\n", "- `mul`: multiply numbers\n", "- `mov`: copy values from memory to CPU registers and back\n", "\n", "You can google all the other commands.\n", "\n", "There is a lot of repetition in the assembly code, because the optimizer unrolls loops over subsequences to make them faster. Using an unrolled loop only works if the remaining chunk of data is large enough. Since the compiler does not know the length of the incoming array, it generates sections which handle shorter chunks and all the code to select which section to use. Finally, there is some code which does the translation from and to Python objects with corresponding error handling.\n", "\n", "We don't need to write SIMD instructions by hand, the optimizer does it for us and in a very sophisticated way." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/roofit/rf101_basics.ipynb0000644000000000000000000000373414332717401017506 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# 101: Basics\n", "\n", "This example corresponds to [RF101](https://root.cern.ch/doc/master/rf101__basics_8py.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import UnbinnedNLL\n", "from numba_stats import truncnorm\n", "from matplotlib import pyplot as plt\n", "import numpy as np\n", "\n", "xrange = (-10, 10)\n", "\n", "def model(x, mu, sigma):\n", " return truncnorm.pdf(x, *xrange, mu, sigma)\n", "\n", "rng = np.random.default_rng(1)\n", "x = rng.normal(1, 3, size=10000)\n", "x = x[(x > xrange[0]) & (x < xrange[1])]\n", "\n", "c = UnbinnedNLL(x, model)\n", "m = Minuit(c, 1, 3)\n", "m.limits[\"mu\"] = (-10, 10)\n", "m.limits[\"sigma\"] = (0.1, 10)\n", "m.migrad()\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(8, 3.5), \n", " sharex=True, constrained_layout=True)\n", "\n", "plt.sca(ax[0])\n", "plt.title(\"Gaussian pdf\")\n", "xm = np.linspace(*xrange, 1000)\n", "plt.plot(xm, model(xm, 1, 1), label=\"sigma=1\")\n", "plt.plot(xm, model(xm, 1, 3), label=\"sigma=3\")\n", "plt.legend()\n", "plt.xlim(*xrange)\n", "\n", "plt.sca(ax[1])\n", "plt.title(\"Gaussian pdf with data\")\n", "m.visualize(bins=100)\n", "plt.xlim(*xrange);" ] } ], "metadata": { "kernelspec": { "display_name": "py39", "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.10.9" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/roofit/rf109_chi2residpull.ipynb0000644000000000000000000000577514332717401021032 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# 109: chi-square residuals and pulls\n", "\n", "This example corresponds to [RF109](https://root.cern.ch/doc/master/rf109__chi2residpull_8py.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from numba_stats import norm\n", "from iminuit.cost import BinnedNLL\n", "import boost_histogram as bh\n", "from matplotlib import pyplot as plt\n", "\n", "# generate a sample of 1000 events with sigma=3\n", "rng = np.random.default_rng(1)\n", "x = rng.normal(scale=3, size=10000)\n", "\n", "# make histogram\n", "h = bh.Histogram(bh.axis.Regular(50, -10, 10))\n", "h.fill(x)\n", "cx = h.axes[0].centers\n", "xe = h.axes[0].edges\n", "\n", "# compute residuals and pulls for wrong Gaussian with sigma = 3.15\n", "pars = [\n", " 0, # mu\n", " 3.15, # sigma\n", "]\n", "\n", "# data model, this is a cdf for a binned analysis\n", "def model(x, mu, sigma):\n", " return norm.cdf(x, mu, sigma)\n", "\n", "# pulls can be computed from the cost functions in iminuit.cost \n", "cost = BinnedNLL(h.values(), xe, model)\n", "pulls = cost.pulls(pars)\n", "# expected count per bin from the model\n", "m = cost.prediction(pars)\n", "# residuals are not generally useful, so there is no library routine and\n", "# we compute them \"by hand\"\n", "residuals = cost.n - m\n", "\n", "# value returned by BinnedNLL functor is chi-square distributed\n", "chi_square = cost(*pars)\n", "# normally, we would subtract fitted degrees of freedom from cost.ndata, but\n", "# in this example nothing no parameters are fitted\n", "print(f\"𝜒²/ndof = {chi_square / cost.ndata:.2f}\")\n", "\n", "fig, ax = plt.subplots(1, 3, figsize=(10, 3),\n", " sharex=True, constrained_layout=True)\n", "\n", "# plot data and gaussian model with sigma = 3.15\n", "plt.sca(ax[0])\n", "plt.title(\"data with distorted gaussian pdf\")\n", "plt.errorbar(cx, h.values(), h.variances() ** 0.5, fmt=\"ok\")\n", "plt.stairs(m, xe, fill=True)\n", "\n", "plt.sca(ax[1])\n", "plt.title(\"residual distribution\")\n", "plt.errorbar(cx, residuals, h.variances() ** 0.5, fmt=\"ok\")\n", "\n", "plt.sca(ax[2])\n", "plt.title(\"pull distribution\")\n", "plt.errorbar(cx, pulls, 1, fmt=\"ok\");" ] } ], "metadata": { "kernelspec": { "display_name": "py310", "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.10.9" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/roofit.ipynb0000644000000000000000000025615014332717401015333 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Fitting RooFit-generated likelihoods\n", "\n", "In this tutorial, we show how to create a negative log-likelihood function with the [RooFit framework](https://root.cern/manual/roofit/) and minimize it with iminuit. \n", "\n", "RooFit is a powerful fitting framework developed by CERN's ROOT team.\n", "RooFit is very powerful and sophisticated, but there are a few reasons to use iminuit instead:\n", "\n", "- RooFit documention is extensive, but lacking in important places\n", "- RooFit interfaces are not Pythonic, they mimic the C++ interfaces (which are also dated)\n", "- Errors are difficult to understand and debug since RooFit is internally executing C++ code\n", "- You may experience segmentation faults when using RooFit from Python due to bugs in the ROOT Python layer (problems with handling life-times of dynamic C++ objects in Python correctly)\n", "\n", "For these reasons, you may consider to transition to iminuit and its cost functions for your project. As a first step, you want to convince yourself that iminuit gives you the same fitting result as you get from RooFit. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Welcome to JupyROOT 6.26/10\n" ] } ], "source": [ "# ROOT is best installed via a conda virtual environment from conda-forge\n", "import ROOT" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# fix PRNG seed for RooFit random number generation\n", "ROOT.RooRandom.randomGenerator().SetSeed(1)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We generate a Gaussian with mean 1 and width 3 and draw 10000 data points from it. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "x = ROOT.RooRealVar(\"x\", \"x\", -10, 10)\n", "mean = ROOT.RooRealVar(\"mean\", \"mean of gaussian\", 1, -10, 10)\n", "sigma = ROOT.RooRealVar(\"sigma\", \"width of gaussian\", 3, 0.1, 10)\n", "\n", "gauss = ROOT.RooGaussian(\"gauss\", \"gaussian PDF\", x, mean, sigma)\n", "\n", "data = gauss.generate({x}, 10000)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We now fit this Gaussian. We use the `createNLL` method and a simple wrapper function `evaluate`. Note that this simple wrapping function does not propagate the parameter names of the Gaussian to iminuit. A future version of iminuit will come with a builtin wrapper that will also propagate the names and limits." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 2.514e+04 Nfcn = 31
EDM = 2.72e-08 (Goal: 0.0001)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 1.003 0.030
1 x1 3.017 0.022
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 0.000926 0 (0.030)
x1 0 (0.030) 0.000497
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 2.514e+04 │ Nfcn = 31 │\n", "│ EDM = 2.72e-08 (Goal: 0.0001) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 1.003 │ 0.030 │ │ │ │ │ │\n", "│ 1 │ x1 │ 3.017 │ 0.022 │ │ │ │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬───────────────────┐\n", "│ │ x0 x1 │\n", "├────┼───────────────────┤\n", "│ x0 │ 0.000926 0 │\n", "│ x1 │ 0 0.000497 │\n", "└────┴───────────────────┘" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from iminuit import Minuit\n", "\n", "nll = gauss.createNLL(data)\n", "\n", "def evaluate(*args):\n", " for par, arg in zip(nll.getVariables(), args):\n", " par.setVal(arg)\n", " # following RooMinimizerFcn.cxx \n", " nll.setHideOffset(False)\n", " r = nll.getVal()\n", " nll.setHideOffset(True)\n", " return r\n", "\n", "evaluate.errordef = Minuit.LIKELIHOOD\n", "\n", "m = Minuit(evaluate, *[p.getVal() for p in nll.getVariables()])\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's compare this to fitting directly with the `fitTo` method." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[#1] INFO:Minimization -- RooAbsMinimizerFcn::setOptimizeConst: activating const optimization\n", " **********\n", " ** 1 **SET PRINT 1\n", " **********\n", " **********\n", " ** 2 **SET NOGRAD\n", " **********\n", " PARAMETER DEFINITIONS:\n", " NO. NAME VALUE STEP SIZE LIMITS\n", " 1 mean 1.00653e+00 2.00000e+00 -1.00000e+01 1.00000e+01\n", " 2 sigma 3.01930e+00 9.90000e-01 1.00000e-01 1.00000e+01\n", " **********\n", " ** 3 **SET ERR 0.5\n", " **********\n", " **********\n", " ** 4 **SET PRINT 1\n", " **********\n", " **********\n", " ** 5 **SET STR 1\n", " **********\n", " NOW USING STRATEGY 1: TRY TO BALANCE SPEED AGAINST RELIABILITY\n", " **********\n", " ** 6 **MIGRAD 1000 1\n", " **********\n", " FIRST CALL TO USER FUNCTION AT NEW START POINT, WITH IFLAG=4.\n", " START MIGRAD MINIMIZATION. STRATEGY 1. CONVERGENCE WHEN EDM .LT. 1.00e-03\n", " FCN=25136.7 FROM MIGRAD STATUS=INITIATE 10 CALLS 11 TOTAL\n", " EDM= unknown STRATEGY= 1 NO ERROR MATRIX \n", " EXT PARAMETER CURRENT GUESS STEP FIRST \n", " NO. NAME VALUE ERROR SIZE DERIVATIVE \n", " 1 mean 1.00653e+00 2.00000e+00 2.02444e-01 3.46428e+01\n", " 2 sigma 3.01930e+00 9.90000e-01 2.22272e-01 2.14205e+01\n", " ERR DEF= 0.5\n", " MIGRAD MINIMIZATION HAS CONVERGED.\n", " MIGRAD WILL VERIFY CONVERGENCE AND ERROR MATRIX.\n", " COVARIANCE MATRIX CALCULATED SUCCESSFULLY\n", " FCN=25136.7 FROM MIGRAD STATUS=CONVERGED 25 CALLS 26 TOTAL\n", " EDM=1.96964e-05 STRATEGY= 1 ERROR MATRIX ACCURATE \n", " EXT PARAMETER STEP FIRST \n", " NO. NAME VALUE ERROR SIZE DERIVATIVE \n", " 1 mean 1.00330e+00 3.04253e-02 3.34944e-04 1.02604e+00\n", " 2 sigma 3.01694e+00 2.22842e-02 5.41347e-04 6.16930e-01\n", " ERR DEF= 0.5\n", " EXTERNAL ERROR MATRIX. NDIM= 25 NPAR= 2 ERR DEF=0.5\n", " 9.257e-04 2.032e-05 \n", " 2.032e-05 4.966e-04 \n", " PARAMETER CORRELATION COEFFICIENTS \n", " NO. GLOBAL 1 2\n", " 1 0.02997 1.000 0.030\n", " 2 0.02997 0.030 1.000\n", " **********\n", " ** 7 **SET ERR 0.5\n", " **********\n", " **********\n", " ** 8 **SET PRINT 1\n", " **********\n", " **********\n", " ** 9 **HESSE 1000\n", " **********\n", " COVARIANCE MATRIX CALCULATED SUCCESSFULLY\n", " FCN=25136.7 FROM HESSE STATUS=OK 10 CALLS 36 TOTAL\n", " EDM=1.96993e-05 STRATEGY= 1 ERROR MATRIX ACCURATE \n", " EXT PARAMETER INTERNAL INTERNAL \n", " NO. NAME VALUE ERROR STEP SIZE VALUE \n", " 1 mean 1.00330e+00 3.04246e-02 6.69888e-05 1.00499e-01\n", " 2 sigma 3.01694e+00 2.22838e-02 1.08269e-04 -4.23243e-01\n", " ERR DEF= 0.5\n", " EXTERNAL ERROR MATRIX. NDIM= 25 NPAR= 2 ERR DEF=0.5\n", " 9.257e-04 1.984e-05 \n", " 1.984e-05 4.966e-04 \n", " PARAMETER CORRELATION COEFFICIENTS \n", " NO. GLOBAL 1 2\n", " 1 0.02926 1.000 0.029\n", " 2 0.02926 0.029 1.000\n", "[#1] INFO:Minimization -- RooAbsMinimizerFcn::setOptimizeConst: deactivating const optimization\n" ] } ], "source": [ "gauss.fitTo(data);" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The results are in agreement, because the results of a fit cannot depend on the minimizer. Technically, RooFit uses a different minimizer than iminuit by default. Unless you change some options, RooFit uses the original MINUIT Fortran implementation translated to C, while iminuit uses the rewritten Minuit2 C++ library." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Just doing the fitting with iminuit does not offer you a lot of advantages. Eventually, you want to switch completely. The equivalent of this exercise in pure Python is the following." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 4.998e+04 Nfcn = 29
EDM = 3e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 mu 0.96 0.03
1 sigma 2.985 0.022
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
mu sigma
mu 0.000906 0 (0.027)
sigma 0 (0.027) 0.000483
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 4.998e+04 │ Nfcn = 29 │\n", "│ EDM = 3e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ mu │ 0.96 │ 0.03 │ │ │ │ │ │\n", "│ 1 │ sigma │ 2.985 │ 0.022 │ │ │ │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────┐\n", "│ │ mu sigma │\n", "├───────┼───────────────────┤\n", "│ mu │ 0.000906 0 │\n", "│ sigma │ 0 0.000483 │\n", "└───────┴───────────────────┘" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from scipy.stats import truncnorm\n", "from iminuit.cost import UnbinnedNLL\n", "import numpy as np\n", "\n", "xrange = (-10., 10.)\n", "\n", "rng = np.random.default_rng(1)\n", "x = rng.normal(1, 3, size=10000)\n", "x = x[(xrange[0] < x) & (x < xrange[1])]\n", "\n", "def model(x, mu, sigma):\n", " zrange = np.subtract(xrange, mu) / sigma\n", " return truncnorm.pdf(x, *zrange, mu, sigma)\n", "\n", "# better use numba_stats.truncnorm, which is simpler to use and faster\n", "#\n", "# from numba_stats import truncnorm\n", "#\n", "# def model(x, mu, sigma):\n", "# return truncnorm.pdf(x, *xrange, mu, sigma)\n", "\n", "nll = UnbinnedNLL(x, model)\n", "m = Minuit(nll, 1, 3)\n", "m.migrad()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We do not get the exact same fitted values as before, since the data sample is different from the one generated by RooFit.\n", "\n", "To get the exact same result, we need to convert the variable `data` which has the type `RooDataSet` into a numpy array. The ROOT Python layer offers the method `to_numpy` for this purpose." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 5.027e+04 Nfcn = 31
EDM = 5.43e-08 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 mu 1.003 0.030
1 sigma 3.017 0.022
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
mu sigma
mu 0.000926 0 (0.030)
sigma 0 (0.030) 0.000497
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 5.027e+04 │ Nfcn = 31 │\n", "│ EDM = 5.43e-08 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ mu │ 1.003 │ 0.030 │ │ │ │ │ │\n", "│ 1 │ sigma │ 3.017 │ 0.022 │ │ │ │ │ │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────┐\n", "│ │ mu sigma │\n", "├───────┼───────────────────┤\n", "│ mu │ 0.000926 0 │\n", "│ sigma │ 0 0.000497 │\n", "└───────┴───────────────────┘" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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W7p0aY94R48ebfE97vQMu/T2M0ahUCA9Pb3X9u7wHHNzc/GrX/0KMTExUCgU2Lx5s8l1URTxwgsvIDo6GoMGDcKUKVNw5swZq59r7/e0PTw2QdHr9bjvvvvw5JNP9npdEATMnDkTer0e+fn5eO+997Bhwwa88MILFj93zpw5KCoqws6dO5GTk4Pc3FzMmzfPGbdgt+TkZFRWVpq8fv/732PUqFG45ZZbLL73scceM3nfypUrXRR13yxbtswk3qeeespi+2eeeQZffvklNm7ciH379qGiogKpqakuitY+p0+fhsFgwNq1a1FUVITXX38da9asweLFi62+V87P8dNPP8WCBQuwZMkSHD58GBMnTsS0adNQXV3da/v8/Hw8+OCDSEtLw5EjRzBr1izMmjULJ06ccHHkttm3bx/S09Oxf/9+7Ny5E+3t7Zg6dSp0Op3F9wUHB5s8swsXLrgo4r4ZO3asSbzffvut2bbu9gy7HDx40OQed+7cCQC47777zL5H7s9Rp9Nh4sSJWL16da/XV65ciTfeeANr1qzBgQMHMHjwYEybNg2tra1mP9Pe72m7iR7u3XffFUNCQnqc37p1q+jj4yNqtVrjuXfeeUcMDg4W29raev2skydPigDEgwcPGs9t27ZNVCgUokajcXjs/aXX68WIiAhx2bJlFtvdfvvtYkZGhmuCcoCRI0eKr7/+us3t6+vrxYEDB4obN240njt16pQIQCwoKHBChI63cuVKcdSoURbbyP053nrrrWJ6errxWBAEMSYmRlyxYkWv7X/zm9+IM2fONDmXmJgoPv74406N01Gqq6tFAOK+ffvMtjH380mulixZIk6cONHm9u7+DLtkZGSI1157rWgwGHq97m7PEYC4adMm47HBYBCjoqLEV155xXiuvr5e9PPzEz/++GOzn2Pv97S9PLYHxZqCggKMHz8ekZGRxnPTpk1DY2MjioqKzL4nNDTUpDdiypQp8PHxwYEDB5wes72++OILXLx4EY8++qjVth9++CHCw8Mxbtw4LFq0CC0tLS6IsO9efvllDB06FDfddBNeeeUVi0NzhYWFaG9vx5QpU4znRo8ejREjRqCgoMAV4fZbQ0MDwsLCrLaT63PU6/UoLCw0eQY+Pj6YMmWK2WdQUFBg0h7o/B51p2cGwOpza25uxsiRIxEXF4d7773X7M8fuThz5gxi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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x = data.to_numpy()[\"x\"]\n", "\n", "nll = UnbinnedNLL(x, model)\n", "m = Minuit(nll, 1, 3)\n", "m.migrad()" ] } ], "metadata": { "kernelspec": { "display_name": "root", "language": "python", "name": "root" }, "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.11.0" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/scipy_and_constraints.ipynb0000644000000000000000000033335114332717401020430 0ustar00{ "cells": [ { "cell_type": "markdown", "id": "ffdfe095", "metadata": {}, "source": [ "# SciPy minimizers and constraints\n", "\n", "The `Minuit` class can call SciPy minimizers implemented in `scipy.optimize.minimize` as alternatives to the standard Migrad minimizer to minimize the cost function. The SciPy minimizers may perform better or worse on some problems. You can give them a try when you are not satisfied with Migrad.\n", "\n", "More importantly, the SciPy minimizers support additional features that Migrad lacks.\n", "\n", "* Migrad does not allow one to use an externally computed hessian matrix.\n", "* Migrad does not allow one to use additional constraints of the form $\\vec a \\leq f(\\vec x) \\leq \\vec b$ in the minimization, where $\\vec x$ is the parameter vector of length $m$, $f$ is an arbitrary function $\\mathcal{R}^m \\rightarrow \\mathcal{R}^k$ and $\\vec a, \\vec b$ are vector bounds with length $k$.\n", "\n", "SciPy comes with a variety of minimization algorithms and some of them support these features. The ability to use constraints is interesting for HEP applications. In particular, it allows us to ensure that a pdf as a function of the parameters is always positive. This can be ensured sometimes with suitable limits on the parameters, but not always.\n", "\n", "We demonstrate this on a common example of fit of an additive model with a signal and background pdf." ] }, { "cell_type": "code", "execution_count": 1, "id": "unnecessary-vermont", "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import ExtendedUnbinnedNLL\n", "import numpy as np\n", "from numba_stats import norm, bernstein\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "import joblib" ] }, { "cell_type": "markdown", "id": "2bac1095", "metadata": {}, "source": [ "The signal pdf is a Gaussian, the background is modelled with second degree Bernstein polynomials. We perform an extended maximum likelihood fit, where the full density model is given by the sum of the signal and background component." ] }, { "cell_type": "code", "execution_count": 2, "id": "equivalent-minnesota", "metadata": {}, "outputs": [], "source": [ "xrange = (0, 1)\n", "\n", "\n", "def model(x, b0, b1, b2, sig, mu, sigma):\n", " beta = [b0, b1, b2]\n", " bint = np.diff(bernstein.integral(xrange, beta, *xrange))\n", " sint = sig * np.diff(norm.cdf(xrange, mu, sigma))[0]\n", " return bint + sint, bernstein.density(x, beta, *xrange) + sig * norm.pdf(x, mu, sigma)" ] }, { "cell_type": "markdown", "id": "58a91a58", "metadata": {}, "source": [ "In searches for rare decays, it is common to fit models like this to small simulated samples that contain only background, to calculate the distribution of some test statistic (usually the likelihood ratio of S+B and B-only hypotheses). Here, for simplicity, we use the signal amplitude itself as the test statistic.\n", "\n", "We run one such fit. The mean and width of the Gaussian are fixed, only the signal amplitude and the background parameters are varied." ] }, { "cell_type": "code", "execution_count": 3, "id": "abroad-census", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = -181.3 Nfcn = 96
EDM = 1.83e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 b0 16 17 0
1 b1 85 35 0
2 b2 16 16 0
3 sig -4.0 2.7
4 mu 0.500 0.005 yes
5 sigma 50.0e-3 0.5e-3 yes
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
b0 b1 b2 sig mu sigma
b0 306 -412 (-0.676) 110 (0.387) 15.1 (0.319) 0 0
b1 -412 (-0.676) 1.21e+03 -366 (-0.645) -62 (-0.659) 0 0
b2 110 (0.387) -366 (-0.645) 266 13.4 (0.305) 0 0
sig 15.1 (0.319) -62 (-0.659) 13.4 (0.305) 7.28 0 0
mu 0 0 0 0 0 0
sigma 0 0 0 0 0 0
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = -181.3 │ Nfcn = 96 │\n", "│ EDM = 1.83e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬───────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼───────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ b0 │ 16 │ 17 │ │ │ 0 │ │ │\n", "│ 1 │ b1 │ 85 │ 35 │ │ │ 0 │ │ │\n", "│ 2 │ b2 │ 16 │ 16 │ │ │ 0 │ │ │\n", "│ 3 │ sig │ -4.0 │ 2.7 │ │ │ │ │ │\n", "│ 4 │ mu │ 0.500 │ 0.005 │ │ │ │ │ yes │\n", "│ 5 │ sigma │ 50.0e-3 │ 0.5e-3 │ │ │ │ │ yes │\n", "└───┴───────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌───────┬───────────────────────────────────────────────────────┐\n", "│ │ b0 b1 b2 sig mu sigma │\n", "├───────┼───────────────────────────────────────────────────────┤\n", "│ b0 │ 306 -412 110 15.1 0 0 │\n", "│ b1 │ -412 1.21e+03 -366 -62 0 0 │\n", "│ b2 │ 110 -366 266 13.4 0 0 │\n", "│ sig │ 15.1 -62 13.4 7.28 0 0 │\n", "│ mu │ 0 0 0 0 0 0 │\n", "│ sigma │ 0 0 0 0 0 0 │\n", "└───────┴───────────────────────────────────────────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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xYo6dOiQxowZo3nz5mnmzJnRqg8AACSQiILF3r17dcMNN2jXrl1KS0tTvXr1NHPmTF188cXRqg8AACSQiILFyJEjo1UHAAAoAJgrBAAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgTUTBYsiQITr//POVkpKi0qVLq2vXrtqwYUO0agMAAAkmomDx6aefqm/fvvr88881a9Ys5eTk6JJLLlFWVla06gMAAAnEF8nGH3300TG3R40apdKlS2vZsmW66KKLrBYGAAAST0TB4n9lZGRIkooXL37cbQKBgAKBQO7tzMzMU9klAADIx/LceTMcDuuee+5RixYtVKdOneNuN2TIEKWlpeUu6enped0lAADI5/IcLPr27avVq1dr3Lhxf7rdgAEDlJGRkbts3749r7sEAAD5XJ5Ohdx5552aNm2a5s+frwoVKvzptn6/X36/P0/FAQCAxBJRsDDG6K677tKUKVM0b948nXPOOdGqCwAAJKCIgkXfvn01ZswYvffee0pJSdHu3bslSWlpaSpcuHBUCgQAAIkjoj4Ww4cPV0ZGhlq3bq1y5crlLuPHj49WfQAAIIFEfCoEAADgeJgrBAAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGANwQIAAFhDsAAAANYQLAAAgDUECwAAYA3BAgAAWEOwAAAA1hAsAACANQQLAABgDcECAABYQ7AAAADWECwAAIA1BAsAAGAN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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "rng = np.random.default_rng(2)\n", "x = rng.uniform(0, 1, size=35)\n", "\n", "cost = ExtendedUnbinnedNLL(x, model)\n", "n = len(x)\n", "m = Minuit(cost, b0=n, b1=n, b2=n, sig=0, mu=0.5, sigma=0.05)\n", "m.print_level = 0\n", "m.limits[\"b0\", \"b1\", \"b2\"] = (0, None)\n", "m.fixed[\"mu\", \"sigma\"] = True\n", "display(m.migrad())\n", "\n", "plt.hist(x, bins=50, density=True)\n", "xm = np.linspace(0, 1)\n", "yint, ym = model(xm, *m.values)\n", "plt.plot(xm, ym / yint);" ] }, { "cell_type": "markdown", "id": "c1fac22f", "metadata": {}, "source": [ "In this example, the signal amplitude came out negative. This happens if the background has an underfluctuation where the signal is expected. This is not an issue if the sum of signal and background density is still positive everywhere where we evaluate it. As long as the total density is positive, individual components are allowed to be negative.\n", "\n", "There are, however, no principle restrictions in this example that prevent the sum of signal and background from becoming negative for some toy data sets. When that happens, the fit will fail, since the total density cannot mathematically become negative.\n", "\n", "If this happens anyway, the fit will fail since taking logarithm of a negative number will cause havoc." ] }, { "cell_type": "markdown", "id": "4ecf9073", "metadata": {}, "source": [ "## Migrad fit on toys\n", "\n", "We apply the fit many times on randomly sampled background-only data to observe this." ] }, { "cell_type": "code", "execution_count": 4, "id": "following-bruce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "28 failed\n" ] } ], "source": [ "@joblib.delayed\n", "def compute(itry):\n", " rng = np.random.default_rng(itry)\n", " x = rng.uniform(0, 1, size=35)\n", " cost = ExtendedUnbinnedNLL(x, model)\n", " m = Minuit(cost, b0=n, b1=n, b2=n, sig=0, mu=0.5, sigma=0.05)\n", " m.limits[\"b0\", \"b1\", \"b2\"] = (0, None)\n", " m.fixed[\"mu\", \"sigma\"] = True\n", " m.migrad()\n", " return m.values[\"sig\"] if m.valid else np.nan\n", "\n", "sigs_migrad = joblib.Parallel(-1)(compute(i) for i in range(200))\n", "\n", "print(np.sum(np.isnan(sigs_migrad)), \"failed\")" ] }, { "cell_type": "code", "execution_count": 5, "id": "bd848895", "metadata": {}, "outputs": [ { "data": { "image/png": "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GjlypMe6xo0bKz4+XiNHjtSyZcv0/PPPq0WLFho8eLD27dt30WsLDQ3VqVOntHbt2loZr0GDBu5bTmVlZTp+/LjOnDmja665Rtu2bfNqrLVr16qgoEDDhw/Xt99+6341aNBACQkJ7ls5R44c0Y4dOzRy5Eg5nU739r/4xS/UoUOHau3r2LFjHrc9amLx4sX6/PPP3VeQvBESEqINGzZo165d2rFjh3bs2KGWLVtq3rx5Ki4u1qRJk/TFF1/olltuUcuWLfXb3/5WhYWF5cZp1qyZTp8+7XHLBvBFhA/gAuTl5SklJUVOp1OvvvqqGjRo4LF+yJAhys3N1eLFi3X77bdr9OjRWr9+vUpKSvTf//3fF72+3/3ud7rqqqvUv39/tWrVSmPGjNHq1asvaMwXXnhBXbp0UXBwsMLDwxUREaGVK1fK5XJ5Nc5XX30lSerTp48iIiI8Xu+++66OHj0qSTpw4ICkH+bV/FR8fHy192eM8aq+HyssLNS0adP0wAMPqHXr1jUaw9/fXx06dFDXrl0VEBCgb7/9Vo888ohmzZolPz8/3XbbbercubPefPNN5ebmavz48ZUegze3fID6iDkfQA25XC71799fBQUF+uijjxQTE+Ox/l//+pdWr16tv/71rx7tYWFhuv76663cu4+MjNSOHTu0Zs0arVq1SqtWrVJmZqbuvPNOvfDCC16P9+KLL2rUqFEaNGiQHnjgAUVGRqpBgwbKyMjQ119/7dVYZWVlkn6Y91HRpM+AgNr75yk8PFzfffddjbefNWuWSkpKNHToUPdD0g4dOiRJ+u6777R//37FxMSUm4hclYceekjdu3fXoEGD9NFHH+nIkSN64oknFBwcrOnTp+vWW29VZmam/P3///+I3333nRo3blzlZGXAFxA+gBooKirSgAED9OWXX+q9996r8PJ/fn6+JOns2bPl1pWWlurMmTO1Vk9V/xMODAzUgAEDNGDAAJWVlel3v/udFi5cqIceekhXXHGFV/t59dVXddlll+n111/32Gd6errXNV9++eWSfghISUlJlfaLi4uT9P9XSn5s79691dpXu3btlJOT43WN5+Tm5uq7774rN6FY+uFhbDNmzND27durPeH2n//8p55//nllZ2dLkg4fPqxmzZopODhYkhQTE6OSkhJ98803ioqKcm+Xk5Oj9u3b1/g4gPqC2y6Al86ePauhQ4cqKytLy5YtU2JiYoX9rrjiCvn7++vll1/2uOR/6NAhffTRR7r66qtrraYmTZpIkgoKCjzajx075rHs7++vLl26SFKNHtV97rbSj49n8+bNysrK8nqs5ORkORwOzZgxQ6WlpeXWn/tWT4sWLdStWze98MILHrd21q5dqy+++KJa+0pMTNTOnTtr/Hjy3//+91q+fLnH69zTRkeNGqXly5erbdu21R5vwoQJuuuuu9SpUydJUlRUlL755hsdP35c0g/fSAoICFDz5s09ttu2bZv7WzKAL+PKB+ClKVOm6K233tKAAQN0/Phxvfjiix7rzz3/ISIiQmPGjNHf/vY39e3bV4MHD9aJEyc0b948nT59WtOmTau1mnr06CHphw/J5ORkNWjQQMOGDdNdd92l48ePq0+fPmrVqpUOHDigOXPmqFu3bjX6H/Rtt92m119/Xb/+9a+VkpKinJwcLViwQB06dNDJkye9GsvhcGj+/Pm644471L17dw0bNkwRERHKzc3VypUrdd111+kvf/mLJCkjI0MpKSm6/vrrNWbMGB0/flxz5sxRx44dq7XfgQMH6rHHHtOGDRvUr18/j3V/+ctfVFBQoMOHD0uSVqxY4b6lMn78eDmdTnXv3l3du3f32O7c7ZeOHTtq0KBB1T7uZcuW6bPPPtNrr73mbktMTFRUVJSGDBmiwYMHa9asWRo8eLDHHKLs7GwdP35cAwcOrPa+gHqrbr9sA/iem266yeOrsz99/VhpaamZM2eO6datm2natKlp2rSpueWWW8z7779frX1V96u2Z86cMePHjzcRERHGz8/PXcerr75q+vXrZyIjI01gYKCJjY019957r/upnt7uu6yszMyYMcPExcWZoKAgc/XVV5u33367wieN6jxftT3ngw8+MMnJycbpdJrg4GBz+eWXm1GjRpmtW7d69HvttddM+/btTVBQkOnQoYN5/fXXvXq8epcuXczYsWMrPM7KzmVVT2P15qu253z//fcmLi7OPPvss+XWbdmyxXTv3t2EhISYAQMGmKNHj3qsf/DBB01sbCyPV8clwc+YC5gCDgA+YsmSJUpLS1Nubq5CQ0PruhyvFBcXq02bNpo6dWqVD6cDfAVzPgD8LIwYMUKxsbHuR9H7kszMTDVs2FD33XdfXZcC1AqufAAAAKu48gEAAKwifAAAAKsI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JyersLBQu3btqnSb0NBQjysJSUlJ8vf31+bNmy96zd546623dOzYsWo9xv7vf/+7mjdvrk6dOmnatGn6/vvvLVRYMzNnzlR4eLiuvvpqPfnkk1XeJsvOzlZpaamSkpLcbe3atVNsbKyysrJslHvBXC6XwsLCztuvvp3DkpISZWdne/zs/f39lZSUVOnPPisry6O/9MN70pfOlaTznq+TJ08qLi5OrVu31sCBAyv996Y++OqrrxQTE6PLLrtMI0aMUG5ubqV9ff38lZSU6MUXX9SYMWOq/EWmvnT+fiwnJ0d5eXke58jpdCohIaHSc1ST93FNXLQnnNZHeXl55R7xfm45Ly+v0m0iIyM92gICAhQWFlbpNnVl0aJFSk5OVqtWrars9x//8R+Ki4tTTEyMPvvsMz344IPau3evXn/9dUuVVt/vf/97de/eXWFhYdq4caOmTZumI0eO6Omnn66wf15engIDA8vN+YmKiqp356si+/bt05w5czRr1qwq+9XHc/jtt9/q7NmzFb7H9uzZU+E2lb0nfeFclZWVaeLEibruuuvUqVOnSvvFx8fr+eefV5cuXeRyuTRr1iz17t1bu3btOu971baEhAQtXrxY8fHxOnLkiKZPn64bbrhBO3fuVEhISLn+vnz+JOmNN95QQUGBRo0aVWkfXzp/P3XuPHhzjmryPq6Jeh8+pk6dqscff7zKPrt37z7vpChfUpNjPnTokNasWaNXXnnlvOP/eL5K586d1aJFC/Xt21dff/21Lr/88poXXk3eHN/kyZPdbV26dFFgYKDuvfdeZWRk1OvHH9fkHP773//WrbfeqiFDhujuu++uctu6PoeQ0tLStHPnzirnREhSYmKiEhMT3cu9e/dW+/bttXDhQj322GMXu0yv9O/f3/3nLl26KCEhQXFxcXrllVc0duzYOqzs4li0aJH69+9f5a+A96Xz50vqffiYMmVKlalUki677LJqjRUdHV1uxu65b0FER0dXus1PJ9mcOXNGx48fr3SbC1WTY87MzFR4eLh+9atfeb2/hIQEST/8r9vGB9eFnNOEhASdOXNG+/fvV3x8fLn10dHRKikpUUFBgcfVj/z8/It2viri7TEePnxYt9xyi3r37q2//vWvXu/P9jmsSPPmzdWgQYNy3yyq6mcfHR3tVf/6Yty4ce7J597+77dhw4a6+uqrtW/fvotUXe0JDQ3VVVddVWmtvnr+JOnAgQN67733vL5a6Evn79x5yM/PV4sWLdzt+fn56tatW4Xb1OR9XCO1NnukHjnfhNP8/Hx328KFC43D4TBFRUUVjnVuwunWrVvdbWvWrKlXE07LyspM27ZtzZQpU2q0/ccff2wkmX/+85+1XFnte/HFF42/v785fvx4hevPTTh99dVX3W179uyp1xNODx06ZK688kozbNgwc+bMmRqNUV/OYc+ePc24cePcy2fPnjUtW7ascsLpbbfd5tGWmJhYbycslpWVmbS0NBMTE2O+/PLLGo1x5swZEx8fbyZNmlTL1dW+EydOmGbNmplnnnmmwvW+dv5+LD093URHR5vS0lKvtqvP50+VTDidNWuWu83lclVrwqk37+Ma1VprI9UDBw4cMNu3bzfTp083TZs2Ndu3bzfbt283J06cMMb88JemU6dOpl+/fmbHjh1m9erVJiIiwkybNs09xubNm018fLw5dOiQu+3WW281V199tdm8ebP5+OOPzZVXXmmGDx9u/fgq89577xlJZvfu3eXWHTp0yMTHx5vNmzcbY4zZt2+fefTRR83WrVtNTk6OefPNN81ll11mbrzxRttln9fGjRvN7NmzzY4dO8zXX39tXnzxRRMREWHuvPNOd5+fHp8xxtx3330mNjbWvP/++2br1q0mMTHRJCYm1sUhnNehQ4fMFVdcYfr27WsOHTpkjhw54n79uI+vnMOXXnrJBAUFmcWLF5svvvjC3HPPPSY0NNT9DbM77rjDTJ061d3/k08+MQEBAWbWrFlm9+7dJj093TRs2NB8/vnndXUIVbr//vuN0+k069ev9zhX33//vbvPT49x+vTpZs2aNebrr7822dnZZtiwYSY4ONjs2rWrLg6hSlOmTDHr1683OTk55pNPPjFJSUmmefPm5ujRo8YY3z9/55w9e9bExsaaBx98sNw6Xzt/J06ccH/WSTJPP/202b59uzlw4IAxxpiZM2ea0NBQ8+abb5rPPvvMDBw40LRt29acPn3aPUafPn3MnDlz3Mvnex/XhksqfIwcOdJIKvf64IMP3H32799v+vfvbxo1amSaN29upkyZ4pF8P/jgAyPJ5OTkuNuOHTtmhg8fbpo2bWocDocZPXq0O9DUB8OHDze9e/eucF1OTo7HzyA3N9fceOONJiwszAQFBZkrrrjCPPDAA8blclmsuHqys7NNQkKCcTqdJjg42LRv397MmDHD4yrVT4/PGGNOnz5tfve735lmzZqZxo0bm1//+tceH+b1SWZmZoV/Z398UdLXzuGcOXNMbGysCQwMND179jSbNm1yr7vpppvMyJEjPfq/8sor5qqrrjKBgYGmY8eOZuXKlZYrrr7KzlVmZqa7z0+PceLEie6fR1RUlPnlL39ptm3bZr/4ahg6dKhp0aKFCQwMNC1btjRDhw41+/btc6/39fN3zpo1a4wks3fv3nLrfO38nfvM+unr3DGUlZWZhx56yERFRZmgoCDTt2/fcscdFxdn0tPTPdqqeh/XBj9jjKm9mzgAAABV+1k95wMAANQ9wgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8ALBi1KhRGjRoUF2XAaAe4CFjAKxwuVwyxnj8wj8AP0+EDwAAYBW3XQDUqldffVWdO3dWo0aNFB4erqSkJJ06darcbZcTJ05oxIgRatKkiVq0aKHZs2fr5ptv1sSJE+usdgB2ED4A1JojR45o+PDhGjNmjHbv3q3169dr8ODBqugC6+TJk/XJJ5/orbfe0tq1a/XRRx9p27ZtdVA1ANsC6roAAJeOI0eO6MyZMxo8eLDi4uIkSZ07dy7X78SJE3rhhRe0dOlS9e3bV5KUmZmpmJgYq/UCqBtc+QBQa7p27aq+ffuqc+fOGjJkiJ577jl999135fr961//UmlpqXr27Oluczqdio+Pt1kugDpC+ABQaxo0aKC1a9dq1apV6tChg+bMmaP4+Hjl5OTUdWkA6hHCB4Ba5efnp+uuu07Tp0/X9u3bFRgYqOXLl3v0ueyyy9SwYUNt2bLF3eZyufTll1/aLhdAHWDOB4Bas3nzZq1bt079+vVTZGSkNm/erG+++Ubt27fXZ5995u4XEhKikSNH6oEHHlBYWJgiIyOVnp4uf39/+fn51eERALCBKx8Aao3D4dCHH36oX/7yl7rqqqv0xz/+UU899ZT69+9fru/TTz+txMRE3XbbbUpKStJ1112n9u3bKzg4uA4qB2ATDxkDUC+cOnVKLVu21FNPPaWxY8fWdTkALiJuuwCoE9u3b9eePXvUs2dPuVwuPfroo5KkgQMH1nFlAC42wgeAOjNr1izt3btXgYGB6tGjhz766CM1b968rssCcJFx2wUAAFjFhFMAAGAV4QMAAFhF+AAAAFYRPgAAgFWEDwAAYBXhAwAAWEX4AAAAVhE+AACAVYQPAABg1f8BFGTB0DTjyXsAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nfailed = np.sum(np.isnan(sigs_migrad))\n", "plt.title(f\"{nfailed} fits failed ({nfailed / len(sigs_migrad) * 100:.0f} %)\")\n", "plt.hist(sigs_migrad, bins=10, range=(-10, 10))\n", "plt.xlabel(\"sig\");" ] }, { "cell_type": "markdown", "id": "432b611e", "metadata": {}, "source": [ "The distribution of the signal amplitude looks fairly gaussian which is good, but the fit failed to converge in a few cases due to the problem just described. Simply discarding these cases is not acceptable, it would distort conclusions drawn from the distribution of the test statistic, which is commonly needed to set limits or to compute the p-value for an observed amplitude.\n", "\n", "We can repair this by placing a limit on the signal amplitude. This is a suitable solution, although it will bias the signal amplitude and change the shape of the distribution of the test statistic. \n", "\n", "An alternative is to perform a constrained minimization, which allows us to directly add a condition to the fit that the model density must be positive at each data point. We merely need to replace the call `m.migrad` with the call `m.scipy` and pass the (non-linear) constraint. An appropriate algorithm is automatically selected which performs a constrained minimization. The SciPy minimizers are fully integrated into Minuit, which means that Minuit computes an EDM value for the minimum and parameter uncertainties." ] }, { "cell_type": "markdown", "id": "d364cb83", "metadata": {}, "source": [ "## SciPy constrained fit on toys \n", "\n", "We run SciPy with the constraint on the same simulated samples on which we ran Migrad before." ] }, { "cell_type": "code", "execution_count": 6, "id": "young-ocean", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 failed\n" ] } ], "source": [ "from scipy.optimize import NonlinearConstraint\n", "\n", "@joblib.delayed\n", "def compute(itry):\n", " rng = np.random.default_rng(itry)\n", " x = rng.uniform(0, 1, size=35)\n", " cost = ExtendedUnbinnedNLL(x, model)\n", " m = Minuit(cost, b0=n, b1=n, b2=n, sig=0, mu=0.5, sigma=0.05)\n", " m.limits[\"b0\", \"b1\", \"b2\"] = (0, None)\n", " m.fixed[\"mu\", \"sigma\"] = True\n", " m.scipy(constraints=NonlinearConstraint(lambda *par: model(x, *par)[1], 0, np.inf))\n", " return m.values[\"sig\"] if m.valid else np.nan\n", "\n", "sigs_constrained = joblib.Parallel(-1)(compute(i) for i in range(200))\n", "\n", "print(np.sum(np.isnan(sigs_constrained)), \"failed\")" ] }, { "cell_type": "code", "execution_count": 7, "id": "0ce87a47", "metadata": {}, "outputs": [ { "data": { "image/png": "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+3Z988036tKli3r16qU9e/ZIkkaNGqUvvvhCS5Ys0fr163XixAn17dv3uhQOAACqJhdHOvfs2dPu/csvv6w5c+Zoy5YtqlevnubPn6+FCxeqS5cukqSkpCQ1bdpUW7Zs0d13311+VQMAgCqrzGM+CgoKtGjRIp0/f15RUVHavn278vLyFB0dbesTERGh0NBQpaSklEuxAACg6nPozIck7d69W1FRUbp06ZJq1qypZcuWqVmzZtq1a5dcXV3l6+tr1z8gIEDp6eklLi8nJ0c5OTm299nZ2Y6WBAAAqhCHw0d4eLh27dqlrKwsffzxx4qNjdX69evLXEBiYqImTZpU5vmBcrM2saIrAICbgsOXXVxdXdW4cWO1adNGiYmJuv322/Xmm28qMDBQubm5yszMtOufkZGhwMDAEpeXkJCgrKws2+vYsWMObwQAAKg6rvk5H4WFhcrJyVGbNm1UvXp1rV692jZt//79Onr0qKKiokqc383NzXbr7uUXAAC4cTl02SUhIUHdunVTaGiozp49q4ULF2rdunVasWKFfHx8FBcXp9GjR8vPz0/e3t565plnFBUVxZ0uAADAxqHwcfLkSQ0aNEhpaWny8fFRy5YttWLFCt13332SpOnTp8vZ2Vn9+vVTTk6OYmJiNHv27OtSOAAAqJqcjDGmoov4vezsbPn4+CgrK4tLMLBWFRxwmvLT6You4aYQFTe1oksAKj1HPr/5bhcAAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKZeKLgAAKruU+WMruoQyiYqbWtElAMXizAcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUcCh+JiYm688475eXlpbp166p3797av3+/XZ9Lly4pPj5e/v7+qlmzpvr166eMjIxyLRoAAFRd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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.title(f\"{np.sum(np.isnan(sigs_constrained))} constrained fits failed\")\n", "plt.hist(sigs_migrad, alpha=0.5, bins=10, range=(-10, 10), label=\"Migrad\")\n", "plt.hist(sigs_constrained, alpha=0.5, bins=10, range=(-10, 10), label=m.fmin.algorithm)\n", "plt.xlabel(\"sig\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "881008cc", "metadata": {}, "source": [ "There are no failures this time. \n", "\n", "For sig > 0, the distributions are identical in this example, as theoretically expected. In practice, there can be small bin migration effects due to finite precision of numerical algorithms. These are not of concern.\n", "\n", "Important are the differences for sig < 0, where Migrad did not converge in a few cases and where therefore samples are missing. Those missing samples are recovered in the distribution produced by the constrained fit.\n", "\n", "This demonstrates that it is important to not discard failed fits, as this will in general distort the distribution of the test statistic." ] }, { "cell_type": "markdown", "id": "f04e7c57", "metadata": {}, "source": [ "## Bonus: unconstrained SciPy fit\n", "\n", "The issues we describe here are of a principal mathematical nature. We should not expect that an unconstrained minimiser from SciPy does better than Migrad, but let's test this assumption. The minimiser that SciPy uses when only box constraints are used is the L-BFGS-B method which is roughly comparable to Migrad. Let us see how well this algorithm does on the same toy samples." ] }, { "cell_type": "code", "execution_count": 8, "id": "a60a75f7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "17 failed\n" ] } ], "source": [ "@joblib.delayed\n", "def compute(itry):\n", " rng = np.random.default_rng(itry)\n", " x = rng.uniform(0, 1, size=35)\n", " cost = ExtendedUnbinnedNLL(x, model)\n", " m = Minuit(cost, b0=n, b1=n, b2=n, sig=0, mu=0.5, sigma=0.05)\n", " m.limits[\"b0\", \"b1\", \"b2\"] = (0, None)\n", " m.fixed[\"mu\", \"sigma\"] = True\n", " m.scipy()\n", " return m.values[\"sig\"] if m.valid else np.nan\n", "\n", "sigs_bfgs = joblib.Parallel(-1)(compute(i) for i in range(200))\n", "\n", "print(np.sum(np.isnan(sigs_bfgs)), \"failed\")" ] }, { "cell_type": "code", "execution_count": 9, "id": "589bf6b1", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.title(f\"{np.sum(np.isnan(sigs_bfgs))} BFGS fits failed\")\n", "plt.hist(sigs_migrad, alpha=0.5, bins=10, range=(-10, 10), label=\"Migrad\")\n", "plt.hist(sigs_constrained, alpha=0.5, bins=10, range=(-10, 10), label=\"SciPy[SLSQS]\")\n", "plt.hist(sigs_bfgs, bins=10, range=(-10, 10), fill=False, label=\"SciPy[L-BFGS-B]\")\n", "plt.xlabel(\"sig\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "933fcadf", "metadata": {}, "source": [ "In this example, the BFGS method actually failed much less than Migrad, but it still fails in some cases, while the constrained fit did not fail at all." ] }, { "cell_type": "markdown", "id": "0a2de7c9", "metadata": {}, "source": [ "## Speed comparison\n", "\n", "Since constrained fits are so useful, should you use them all the time? Probably not.\n", "\n", "Constrained fits are more computationally expensive. Satisfying extra constrains generally slows down convergence. Let's compare the speed of the three minimisers tested here. We set the strategy to 0, to avoid computing the Hessian matrix automatically, since we want to measure only the time used by the minimiser." ] }, { "cell_type": "code", "execution_count": 10, "id": "84dfd255", "metadata": {}, "outputs": [], "source": [ "m.strategy = 0" ] }, { "cell_type": "code", "execution_count": 11, "id": "6e6c7b2b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.37 ms ± 555 µs per loop (mean ± std. dev. of 7 runs, 3 loops each)\n" ] } ], "source": [ "%timeit -n3 m.reset(); m.migrad()" ] }, { "cell_type": "code", "execution_count": 12, "id": "bafa5a40", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4.67 ms ± 713 µs per loop (mean ± std. dev. of 7 runs, 3 loops each)\n" ] } ], "source": [ "%timeit -n3 m.reset(); m.scipy()" ] }, { "cell_type": "code", "execution_count": 13, "id": "05a2b917", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11.4 ms ± 283 µs per loop (mean ± std. dev. of 7 runs, 3 loops each)\n" ] } ], "source": [ "%timeit -n3 m.reset(); m.scipy(constraints=NonlinearConstraint(lambda *par: model(x, *par)[1], 0, np.inf))" ] }, { "cell_type": "markdown", "id": "365a71bf", "metadata": {}, "source": [ "Migrad is the fastest, followed by the L-BFGS-B method. The constrained fit is much slower.\n", "\n", "The constrained fit is much slower, since it has to do more work. Why Migrad is faster than L-BFGS-B is not so obvious. There are some general reasons for that, but there may be cases where L-BFGS-B performs better.\n", "\n", "Migrad is comparably fast because of its smart stopping criterion. Migrad stops the fit as soon as the improvement of the fitted parameters become small compared to their uncertainties. Migrad was explicitly designed for statistical fits, where the cost function is a log-likelihood or least-squares function. Since it assumes that, it can stops the fit as soon as the parameter improvements become negligible compared to the parameter uncertainty, which is given by the inverse of its internal approximation of the Hessian matrix.\n", "\n", "The SciPy minimisers do not expect the cost function to be a log-likelihood or least-squares and thus cannot assume that the Hessian matrix has a special meaning. Instead they tend to optimise until they hit the limits of machine precision. This is the main reason why the L-BFGS-B method is slower. You can also see this in the benchmark section of the documentation.\n", "\n", "We can force Migrad to do something similar by setting the tolerance to a tiny value." ] }, { "cell_type": "code", "execution_count": 14, "id": "70327e76", "metadata": {}, "outputs": [], "source": [ "m.tol = 1e-20" ] }, { "cell_type": "code", "execution_count": 15, "id": "3cb6d132", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.89 ms ± 364 µs per loop (mean ± std. dev. of 7 runs, 3 loops each)\n" ] } ], "source": [ "%timeit -n3 m.reset(); m.migrad()" ] }, { "cell_type": "markdown", "id": "178aaef6", "metadata": {}, "source": [ "Now the runtime of Migrad is closer to L-BFGS-B, but it is still faster in this case." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 5 } iminuit-2.24.0/doc/notebooks/simultaneous_fits.ipynb0000644000000000000000000000763414332717401017607 0ustar00{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Fits with shared parameters\n", "\n", "We demonstrate how to simultaneously fit two datasets with different models that shares a common parameter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import UnbinnedNLL\n", "from iminuit.util import describe\n", "from matplotlib import pyplot as plt\n", "import numpy as np\n", "from numba_stats import norm" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# generate two data sets which are fitted simultaneously\n", "rng = np.random.default_rng(1)\n", "\n", "width = 2.0\n", "data1 = rng.normal(0, width, size=1000)\n", "data2 = rng.normal(5, width, size=1000)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use two pdfs with different names for non-shared parameters,\n", "# so that they are fitted independently\n", "\n", "def pdf1(x, μ_1, σ):\n", " return norm.pdf(x, μ_1, σ)\n", "\n", "def pdf2(x, μ_2, σ):\n", " return norm.pdf(x, μ_2, σ)\n", "\n", "# combine two log-likelihood functions by adding them\n", "lh = UnbinnedNLL(data1, pdf1) + UnbinnedNLL(data2, pdf2)\n", "\n", "print(f\"{describe(lh)=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `σ` parameter is shared between the data sets, while the means of the two normal distributions are independently fitted." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot(cost, xe, minuit, ax, **style):\n", " signature = describe(cost)\n", " data = cost.data\n", " \n", " values = minuit.values[signature]\n", " errors = minuit.errors[signature]\n", "\n", " cx = (xe[1:] + xe[:-1]) / 2\n", "\n", " ym = np.diff(norm.cdf(xe, *values)) * np.sum(w)\n", " t = []\n", " for n, v, e in zip(signature, values, errors):\n", " t.append(f\"${n} = {v:.3f} ± {e:.3f}$\")\n", " ax.plot(cx, ym, label=\"\\n\".join(t), **style)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Minuit(lh, μ_1=1, μ_2=2, σ=1)\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(14, 5))\n", "\n", "hists = [np.histogram(lhi.data, bins=50) for lhi in lh]\n", "\n", "# draw data and model with initial parameters\n", "for lhi, (w, xe), axi in zip(lh, hists, ax):\n", " cx = (xe[1:] + xe[:-1]) / 2\n", " axi.errorbar(cx, w, np.sqrt(w), fmt=\"ok\", capsize=0, zorder=0)\n", " plot(lhi, xe, m, axi, ls=\"--\")\n", "\n", "m.migrad()\n", "\n", "# draw model with fitted parameters\n", "for lhi, (w, xe), axi in zip(lh, hists, ax):\n", " plot(lhi, xe, m, axi)\n", " axi.legend()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dashed line shows the initial model before the fit, the solid line shows the model after the fit. Note that the σ parameter is shared." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.14 ('venv': venv)", "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.10.8" }, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 4 } iminuit-2.24.0/doc/notebooks/template_fits.ipynb0000644000000000000000000121322714332717401016670 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Template fits\n", "\n", "In applications we are interested in separating a signal component from background components, we often fit parameteric models to data. Sometimes constructing a parametric model for some component is difficult. In that case, one fits a template instead which may be obtained from simulation or from a calibration sample in which a pure component can be isolated.\n", "\n", "The challenge then is to propagate the uncertainty of the template into the result. The template is now also estimated from a sample (be it simulated or a calibration sample), and the uncertainty associated to that can be substantial. We investigate different approaches for template fits, including the Barlow-Beeston and Barlow-Beeston-lite methods.\n", "\n", "**Note:** This work has been published: [H. Dembinski, A. Abdelmotteleb, Eur.Phys.J.C 82 (2022) 11, 1043](https://doi.org/10.1140/epjc/s10052-022-11019-z)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import poisson_chi2, Template, ExtendedBinnedNLL\n", "import numpy as np\n", "from scipy.stats import norm, truncexpon\n", "from scipy.optimize import root_scalar, minimize\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "from collections import defaultdict\n", "from joblib import Parallel, delayed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a toy example, we generate a mixture of two components: a normally distributed signal and exponentially distributed background." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def generate(rng, nmc, truth, bins):\n", " xe = np.linspace(0, 2, bins + 1)\n", " b = np.diff(truncexpon(1, 0, 2).cdf(xe))\n", " s = np.diff(norm(1, 0.1).cdf(xe))\n", " n = rng.poisson(b * truth[0]) + rng.poisson(s * truth[1])\n", " t = np.array([rng.poisson(b * nmc), rng.poisson(s * nmc)])\n", " return xe, n, t\n", "\n", "rng = np.random.default_rng(1)\n", "truth = 750, 250\n", "xe, n, t = generate(rng, 100, truth, 15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Data is visualized on the left-hand side. The templates are shown on the right-hand side. To show the effect of uncertainties in the template, this example intentially uses templates with poor statistical resolution." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(10, 4), sharex=True)\n", "ax[0].stairs(n, xe, fill=True, color=\"k\", alpha=0.5, label=\"data\")\n", "for i, ti in enumerate(t):\n", " ax[1].stairs(ti, xe, fill=True, alpha=0.5, label=f\"template {i}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Bootstrapping template uncertainties\n", "\n", "Bootstrapping is a general purpose technique to include uncertainties backed up by bootstrap theory, so it can be applied to this problem.\n", "We perform a standard fit and pretend that the templates have no uncertainties. Then, we repeat this fit many times with templates that are fluctuated around the actual values assuming a Poisson distribution.\n", "\n", "Here is the cost function." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 4.338e+04 (chi2/ndof = 3336.6) Nfcn = 110
EDM = 3.77e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 761 30 0
1 x1 193 19 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 933 -172 (-0.294)
x1 -172 (-0.294) 365
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 4.338e+04 (chi2/ndof = 3336.6)│ Nfcn = 110 │\n", "│ EDM = 3.77e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 761 │ 30 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 193 │ 19 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬───────────┐\n", "│ │ x0 x1 │\n", "├────┼───────────┤\n", "│ x0 │ 933 -172 │\n", "│ x1 │ -172 365 │\n", "└────┴───────────┘" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def cost(yields):\n", " mu = 0\n", " for y, c in zip(yields, t):\n", " mu += y * c / np.sum(c)\n", " r = poisson_chi2(n, mu)\n", " return r\n", "\n", "cost.errordef = Minuit.LEAST_SQUARES\n", "cost.ndata = np.prod(n.shape)\n", "\n", "starts = np.ones(2)\n", "m = Minuit(cost, starts)\n", "m.limits = (0, None)\n", "m.migrad()\n", "m.hesse()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The uncertainties reported by the fit correspond to the uncertainty in the data, but not the uncertainty in the templates. The chi2/ndof is also very large, since the uncertainties in the template are not considered in the fit.\n", "\n", "We bootstrap the templates 1000 times and compute the covariance of the fitted results." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "b = 100\n", "rng = np.random.default_rng(1)\n", "pars = []\n", "for ib in range(b):\n", " ti = rng.poisson(t)\n", "\n", " def cost(yields):\n", " mu = 0\n", " for y, c in zip(yields, ti):\n", " mu += y * c / np.sum(c)\n", " r = poisson_chi2(n, mu)\n", " return r\n", " \n", " mi = Minuit(cost, m.values[:])\n", " mi.errordef = Minuit.LEAST_SQUARES\n", " mi.limits = (0, None)\n", " mi.strategy = 0\n", " mi.migrad()\n", " assert mi.valid\n", " pars.append(mi.values[:])\n", "\n", "cov2 = np.cov(np.transpose(pars), ddof=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We print the uncertainties from the different stages and the correlation between the two yields.\n", "\n", "To obtain the full error, we add the independent covariance matrices from the original fit and the bootstrap." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fit\n", " b 761 +- 31\n", " s 193 +- 19\n", " correlation -0.29\n", "bootstrap\n", " b 761 +- 36\n", " s 193 +- 36\n", " correlation -0.98\n", "fit+bootstrap\n", " b 761 +- 47\n", " s 193 +- 40\n", " correlation -0.75\n" ] } ], "source": [ "cov1 = m.covariance\n", "\n", "for title, cov in zip((\"fit\", \"bootstrap\", \"fit+bootstrap\"), \n", " (cov1, cov2, cov1 + cov2)):\n", " print(title)\n", " for label, p, e in zip((\"b\", \"s\"), m.values, np.diag(cov) ** 0.5):\n", " print(f\" {label} {p:.0f} +- {e:.0f}\")\n", " print(f\" correlation {cov[0, 1] / np.prod(np.diag(cov)) ** 0.5:.2f}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The bootstrapped template errors are much larger than the fit errors in this case, since the sample used to generate the templates is much smaller than the data sample.\n", "\n", "The bootstrapped errors for both yields are nearly equal (they become exactly equal if the template sample is large) and the correlation is close to -1 (and becomes exactly -1 in large samples). This is expected, since the data sample is fixed in each iteration. Under these conditions, a change in the templates can only increase the yield of one component at an equal loss for the other component." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Template fit with nuisance parameters\n", "\n", "As described in [Barlow and Beeston, Comput.Phys.Commun. 77 (1993) 219-228](https://doi.org/10.1016/0010-4655(93)90005-W), the correct treatment from first principles is to write down the likelihood function for this case, in which the observed values and unknown parameters are clearly stated. The insight is that the true contents of the bins for the templates are unknown and we need to introduce a nuisance parameter for each bin entry in the template. The combined likelihood for the problem is then combines the estimation of the template yields with the estimation of unknown templates.\n", "\n", "This problem can be handled straight-forwardly with Minuit, but it leads to the introduction of a large number of nuisance parameters, one for each entry in each template. We again write a cost function for this case (here a class for convenience).\n", "\n", "As a technical detail, it is necessary to increase the call limit in Migrad for the fit to fully converge, since the limit set by Minuit's default heuristic is too tight for this application." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 18.27 (chi2/ndof = 1.4) Nfcn = 3464
EDM = 6.02e-05 (Goal: 0.0002)
Valid Minimum SOME Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 800 50 0
1 x1 190 40 0
2 x2 9.0 1.4 0
3 x3 8.5 1.3 0
4 x4 9.0 1.4 0
5 x5 7.4 1.2 0
6 x6 8.4 1.3 0
7 x7 6.4 1.1 0
8 x8 9.2 1.7 0
9 x9 4.8 2.3 0
10 x10 7.0 1.5 0
11 x11 5.5 1.0 0
12 x12 5.1 0.9 0
13 x13 4.6 0.9 0
14 x14 4.7 0.9 0
15 x15 4.3 0.8 0
16 x16 3.2 0.7 0
17 x17 0.0 0.4 0
18 x18 0.0 0.4 0
19 x19 0.0 0.4 0
20 x20 0.0 0.5 0
21 x21 0.0 0.4 0
22 x22 2.1 1.5 0
23 x23 19 4 0
24 x24 54 7 0
25 x25 23 4 0
26 x26 1.1 1.0 0
27 x27 0.0 0.4 0
28 x28 0.0 0.4 0
29 x29 0.0 0.4 0
30 x30 0.0 0.4 0
31 x31 0.0 0.5 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31
x0 2.24e+03 -1.45e+03 (-0.756) -14.6 (-0.226) -13.8 (-0.222) -14.6 (-0.226) -12 (-0.214) -13.6 (-0.221) -5.25 (-0.100) 23.6 (0.289) 68.9 (0.644) 23.2 (0.320) -6.29 (-0.134) -8.3 (-0.191) -7.42 (-0.184) -7.59 (-0.185) -7.06 (-0.181) -5.12 (-0.161) 2.82e-08 -0 -1.04e-06 -2.67e-06 -0 -5.71 (-0.081) -26.2 (-0.131) 29.8 (0.089) 5.32 (0.026) -3.16 (-0.062) -0 -0 -0 -0 -2.52e-05
x1 -1.45e+03 (-0.756) 1.64e+03 14.6 (0.264) 13.8 (0.260) 14.6 (0.264) 12 (0.250) 13.6 (0.259) 5.25 (0.117) -23.6 (-0.338) -68.9 (-0.754) -23.2 (-0.375) 6.29 (0.157) 8.3 (0.223) 7.42 (0.215) 7.59 (0.217) 7.06 (0.212) 5.12 (0.188) -2.72e-08 0 1.03e-06 2.66e-06 0 5.71 (0.095) 26.2 (0.153) -29.8 (-0.104) -5.32 (-0.030) 3.16 (0.073) 0 0 0 0 2.52e-05
x2 -14.6 (-0.226) 14.6 (0.264) 1.88 0.842 (0.470) 0.895 (0.477) 0.734 (0.452) 0.831 (0.468) 0.584 (0.384) 0.525 (0.222) -0.302 (-0.098) 0.345 (0.165) 0.522 (0.384) 0.507 (0.403) 0.453 (0.389) 0.464 (0.392) 0.432 (0.382) 0.313 (0.340) 2.37e-09 0 9.87e-09 2.43e-08 0 0.0578 (0.028) 0.264 (0.046) -0.3 (-0.031) -0.0538 (-0.009) 0.0319 (0.022) 0 0 0 0 2.62e-07
x3 -13.8 (-0.222) 13.8 (0.260) 0.842 (0.470) 1.71 0.842 (0.470) 0.69 (0.445) 0.781 (0.460) 0.549 (0.378) 0.494 (0.218) -0.284 (-0.096) 0.324 (0.162) 0.491 (0.378) 0.477 (0.397) 0.426 (0.382) 0.436 (0.385) 0.406 (0.376) 0.294 (0.334) -1.08e-10 -0 8.99e-09 2.28e-08 0 0.0543 (0.028) 0.248 (0.045) -0.282 (-0.031) -0.0505 (-0.009) 0.03 (0.021) 0 0 0 0 2.48e-07
x4 -14.6 (-0.226) 14.6 (0.264) 0.895 (0.477) 0.842 (0.470) 1.88 0.734 (0.452) 0.831 (0.468) 0.584 (0.384) 0.525 (0.222) -0.302 (-0.098) 0.345 (0.165) 0.522 (0.384) 0.507 (0.403) 0.453 (0.389) 0.464 (0.392) 0.432 (0.382) 0.313 (0.340) -1.07e-10 0 -7.03e-08 2.45e-08 0 0.0578 (0.028) 0.264 (0.046) -0.3 (-0.031) -0.0537 (-0.009) 0.0319 (0.022) 0 0 0 0 2.62e-07
x5 -12 (-0.214) 12 (0.250) 0.734 (0.452) 0.69 (0.445) 0.734 (0.452) 1.4 0.681 (0.443) 0.478 (0.364) 0.431 (0.210) -0.248 (-0.092) 0.283 (0.156) 0.428 (0.364) 0.416 (0.382) 0.372 (0.368) 0.38 (0.371) 0.354 (0.362) 0.256 (0.322) -9.87e-11 0 7.25e-09 -1.65e-07 0 0.0473 (0.027) 0.216 (0.043) -0.246 (-0.029) -0.0441 (-0.009) 0.0262 (0.021) 0 0 0 0 2.16e-07
x6 -13.6 (-0.221) 13.6 (0.259) 0.831 (0.468) 0.781 (0.460) 0.831 (0.468) 0.681 (0.443) 1.68 0.541 (0.376) 0.487 (0.218) -0.28 (-0.096) 0.32 (0.162) 0.484 (0.377) 0.471 (0.396) 0.421 (0.381) 0.431 (0.384) 0.401 (0.375) 0.29 (0.333) -1.17e-10 0 8.8e-09 2.29e-08 -0 0.0536 (0.028) 0.245 (0.045) -0.278 (-0.030) -0.0499 (-0.009) 0.0296 (0.021) 0 0 0 0 2.43e-07
x7 -5.25 (-0.100) 5.25 (0.117) 0.584 (0.384) 0.549 (0.378) 0.584 (0.384) 0.478 (0.364) 0.541 (0.376) 1.23 0.421 (0.220) -0.0401 (-0.016) 0.295 (0.174) 0.346 (0.314) 0.331 (0.325) 0.295 (0.313) 0.302 (0.315) 0.281 (0.307) 0.204 (0.273) -1.31e-11 0 5.87e-09 1.39e-08 0 -0.424 (-0.258) 0.207 (0.044) 0.116 (0.015) 0.0813 (0.017) 0.0193 (0.016) 0 0 0 0 1.4e-07
x8 23.6 (0.289) -23.6 (-0.338) 0.525 (0.222) 0.494 (0.218) 0.525 (0.222) 0.431 (0.210) 0.487 (0.218) 0.421 (0.220) 2.99 0.999 (0.256) 0.732 (0.277) 0.348 (0.203) 0.298 (0.188) 0.266 (0.181) 0.272 (0.182) 0.253 (0.178) 0.184 (0.158) 4.8e-11 0 1.44e-09 -1.43e-09 0 0.0195 (0.008) -3.03 (-0.416) 2.16 (0.177) 0.842 (0.112) 0.00754 (0.004) 0 0 0 0 -7.48e-08
x9 68.9 (0.644) -68.9 (-0.754) -0.302 (-0.098) -0.284 (-0.096) -0.302 (-0.098) -0.248 (-0.092) -0.28 (-0.096) -0.0401 (-0.016) 0.999 (0.256) 5.1 0.934 (0.271) -0.0941 (-0.042) -0.171 (-0.083) -0.153 (-0.079) -0.157 (-0.080) -0.146 (-0.078) -0.106 (-0.070) 1.73e-10 0 -1.09e-08 -3.64e-08 0 -0.048 (-0.014) 0.432 (0.045) -2.1 (-0.132) 1.75 (0.178) -0.0329 (-0.014) 0 0 0 0 -5.36e-07
x10 23.2 (0.320) -23.2 (-0.375) 0.345 (0.165) 0.324 (0.162) 0.345 (0.165) 0.283 (0.156) 0.32 (0.162) 0.295 (0.174) 0.732 (0.277) 0.934 (0.271) 2.33 0.238 (0.157) 0.195 (0.139) 0.175 (0.134) 0.179 (0.135) 0.166 (0.132) 0.12 (0.117) 1.13e-12 0 4.71e-11 -4.1e-09 0 0.00938 (0.004) 0.336 (0.052) 1.97 (0.183) -2.32 (-0.349) 0.0023 0 0 0 0 -1.01e-07
x11 -6.29 (-0.134) 6.29 (0.157) 0.522 (0.384) 0.491 (0.378) 0.522 (0.384) 0.428 (0.364) 0.484 (0.377) 0.346 (0.314) 0.348 (0.203) -0.0941 (-0.042) 0.238 (0.157) 0.984 0.296 (0.325) 0.264 (0.313) 0.271 (0.315) 0.252 (0.307) 0.182 (0.273) -9.79e-11 0 5.47e-09 1.33e-08 0 0.0327 (0.022) 0.172 (0.041) -0.0118 (-0.002) 0.0295 (0.007) -0.223 (-0.211) 0 0 0 0 1.37e-07
x12 -8.3 (-0.191) 8.3 (0.223) 0.507 (0.403) 0.477 (0.397) 0.507 (0.403) 0.416 (0.382) 0.471 (0.396) 0.331 (0.325) 0.298 (0.188) -0.171 (-0.083) 0.195 (0.139) 0.296 (0.325) 0.843 0.257 (0.328) 0.263 (0.331) 0.245 (0.323) 0.177 (0.287) -7.11e-11 0 4.86e-09 1.39e-08 0 0.0327 (0.024) 0.149 (0.039) -0.17 (-0.026) -0.0305 (-0.008) 0.0181 -0 0 0 0 1.5e-07
x13 -7.42 (-0.184) 7.42 (0.215) 0.453 (0.389) 0.426 (0.382) 0.453 (0.389) 0.372 (0.368) 0.421 (0.381) 0.295 (0.313) 0.266 (0.181) -0.153 (-0.079) 0.175 (0.134) 0.264 (0.313) 0.257 (0.328) 0.726 0.235 (0.319) 0.219 (0.311) 0.158 (0.277) -6.73e-11 0 4.78e-09 1.24e-08 0 0.0292 (0.023) 0.133 (0.037) -0.152 (-0.025) -0.0272 (-0.007) 0.0162 (0.018) 0 -0 0 0 1.34e-07
x14 -7.59 (-0.185) 7.59 (0.217) 0.464 (0.392) 0.436 (0.385) 0.464 (0.392) 0.38 (0.371) 0.431 (0.384) 0.302 (0.315) 0.272 (0.182) -0.157 (-0.080) 0.179 (0.135) 0.271 (0.315) 0.263 (0.331) 0.235 (0.319) 0.749 0.224 (0.313) 0.162 (0.279) -5.43e-11 0 4.48e-09 1.29e-08 0 0.0299 (0.023) 0.137 (0.037) -0.155 (-0.025) -0.0279 (-0.007) 0.0165 (0.018) 0 0 -0 0 1.37e-07
x15 -7.06 (-0.181) 7.06 (0.212) 0.432 (0.382) 0.406 (0.376) 0.432 (0.382) 0.354 (0.362) 0.401 (0.375) 0.281 (0.307) 0.253 (0.178) -0.146 (-0.078) 0.166 (0.132) 0.252 (0.307) 0.245 (0.323) 0.219 (0.311) 0.224 (0.313) 0.681 0.151 (0.272) -5.74e-11 0 4.48e-09 1.21e-08 0 0.0278 (0.023) 0.127 (0.037) -0.145 (-0.025) -0.0259 (-0.007) 0.0154 (0.017) 0 0 0 -0 1.27e-07
x16 -5.12 (-0.161) 5.12 (0.188) 0.313 (0.340) 0.294 (0.334) 0.313 (0.340) 0.256 (0.322) 0.29 (0.333) 0.204 (0.273) 0.184 (0.158) -0.106 (-0.070) 0.12 (0.117) 0.182 (0.273) 0.177 (0.287) 0.158 (0.277) 0.162 (0.279) 0.151 (0.272) 0.452 -7.57e-11 0 3.32e-09 9.14e-09 0 0.0202 0.0921 (0.033) -0.105 (-0.022) -0.0188 (-0.006) 0.0111 (0.016) 0 0 0 0 -1.25e-06
x17 2.82e-08 -2.72e-08 2.37e-09 -1.08e-10 -1.07e-10 -9.87e-11 -1.17e-10 -1.31e-11 4.8e-11 1.73e-10 1.13e-12 -9.79e-11 -7.11e-11 -6.73e-11 -5.43e-11 -5.74e-11 -7.57e-11 4.96e-09 0 -1.06e-15 4.24e-14 -0 -6.61e-11 3.35e-10 8.66e-10 4.04e-10 6.92e-12 0 0 -0 -0 -1.87e-13
x18 -0 0 0 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 -0 0 0 0 0 0 -0 -0 0 -0 0
x19 -1.04e-06 1.03e-06 9.87e-09 8.99e-09 -7.03e-08 7.25e-09 8.8e-09 5.87e-09 1.44e-09 -1.09e-08 4.71e-11 5.47e-09 4.86e-09 4.78e-09 4.48e-09 4.48e-09 3.32e-09 -1.06e-15 0 5.45e-07 -1.29e-13 0 6.39e-10 2.48e-09 -1.99e-08 -6.18e-09 -2.06e-10 0 0 0 0 -8.77e-13
x20 -2.67e-06 2.66e-06 2.43e-08 2.28e-08 2.45e-08 -1.65e-07 2.29e-08 1.39e-08 -1.43e-09 -3.64e-08 -4.1e-09 1.33e-08 1.39e-08 1.24e-08 1.29e-08 1.21e-08 9.14e-09 4.24e-14 -0 -1.29e-13 6.59e-07 -0 1.99e-09 5.74e-10 -7.53e-08 -2.41e-08 1.21e-09 -0 -0 -0 -0 6.71e-13
x21 -0 0 0 0 0 0 -0 0 0 0 0 0 0 0 0 0 0 -0 -0 0 -0 0 0 0 0 0 0 -0 -0 -0 -0 0
x22 -5.71 (-0.081) 5.71 (0.095) 0.0578 (0.028) 0.0543 (0.028) 0.0578 (0.028) 0.0473 (0.027) 0.0536 (0.028) -0.424 (-0.258) 0.0195 (0.008) -0.048 (-0.014) 0.00938 (0.004) 0.0327 (0.022) 0.0327 (0.024) 0.0292 (0.023) 0.0299 (0.023) 0.0278 (0.023) 0.0202 -6.61e-11 0 6.39e-10 1.99e-09 0 2.19 0.0106 (0.002) -0.0762 (-0.007) -0.0247 (-0.004) 0.00233 0 0 0 0 2.55e-08
x23 -26.2 (-0.131) 26.2 (0.153) 0.264 (0.046) 0.248 (0.045) 0.264 (0.046) 0.216 (0.043) 0.245 (0.045) 0.207 (0.044) -3.03 (-0.416) 0.432 (0.045) 0.336 (0.052) 0.172 (0.041) 0.149 (0.039) 0.133 (0.037) 0.137 (0.037) 0.127 (0.037) 0.0921 (0.033) 3.35e-10 0 2.48e-09 5.74e-10 0 0.0106 (0.002) 17.8 0.947 (0.032) 0.371 (0.020) 0.00444 (0.001) 0 0 0 0 -1.79e-08
x24 29.8 (0.089) -29.8 (-0.104) -0.3 (-0.031) -0.282 (-0.031) -0.3 (-0.031) -0.246 (-0.029) -0.278 (-0.030) 0.116 (0.015) 2.16 (0.177) -2.1 (-0.132) 1.97 (0.183) -0.0118 (-0.002) -0.17 (-0.026) -0.152 (-0.025) -0.155 (-0.025) -0.145 (-0.025) -0.105 (-0.022) 8.66e-10 0 -1.99e-08 -7.53e-08 0 -0.0762 (-0.007) 0.947 (0.032) 49.8 3.46 (0.113) -0.0548 (-0.007) 0 0 0 0 -9.96e-07
x25 5.32 (0.026) -5.32 (-0.030) -0.0538 (-0.009) -0.0505 (-0.009) -0.0537 (-0.009) -0.0441 (-0.009) -0.0499 (-0.009) 0.0813 (0.017) 0.842 (0.112) 1.75 (0.178) -2.32 (-0.349) 0.0295 (0.007) -0.0305 (-0.008) -0.0272 (-0.007) -0.0279 (-0.007) -0.0259 (-0.007) -0.0188 (-0.006) 4.04e-10 0 -6.18e-09 -2.41e-08 0 -0.0247 (-0.004) 0.371 (0.020) 3.46 (0.113) 18.9 -0.0184 (-0.004) 0 0 0 0 -3.5e-07
x26 -3.16 (-0.062) 3.16 (0.073) 0.0319 (0.022) 0.03 (0.021) 0.0319 (0.022) 0.0262 (0.021) 0.0296 (0.021) 0.0193 (0.016) 0.00754 (0.004) -0.0329 (-0.014) 0.0023 -0.223 (-0.211) 0.0181 0.0162 (0.018) 0.0165 (0.018) 0.0154 (0.017) 0.0111 (0.016) 6.92e-12 0 -2.06e-10 1.21e-09 0 0.00233 0.00444 (0.001) -0.0548 (-0.007) -0.0184 (-0.004) 1.14 0 0 0 0 1.54e-08
x27 -0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 0 0 0 -0 0 -0 -0 0 0 0 0 0 0 -0 -0 -0 0
x28 -0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 0 0 -0 0 -0 -0 0 0 0 0 0 -0 0 -0 -0 0
x29 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 -0 0 0 -0 -0 0 0 0 0 0 -0 -0 0 -0 0
x30 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 -0 -0 0 -0 -0 0 0 0 0 0 -0 -0 -0 0 0
x31 -2.52e-05 2.52e-05 2.62e-07 2.48e-07 2.62e-07 2.16e-07 2.43e-07 1.4e-07 -7.48e-08 -5.36e-07 -1.01e-07 1.37e-07 1.5e-07 1.34e-07 1.37e-07 1.27e-07 -1.25e-06 -1.87e-13 0 -8.77e-13 6.71e-13 0 2.55e-08 -1.79e-08 -9.96e-07 -3.5e-07 1.54e-08 0 0 0 0 5.19e-06
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 18.27 (chi2/ndof = 1.4) │ Nfcn = 3464 │\n", "│ EDM = 6.02e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ SOME Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 800 │ 50 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 190 │ 40 │ │ │ 0 │ │ │\n", "│ 2 │ x2 │ 9.0 │ 1.4 │ │ │ 0 │ │ │\n", "│ 3 │ x3 │ 8.5 │ 1.3 │ │ │ 0 │ │ │\n", "│ 4 │ x4 │ 9.0 │ 1.4 │ │ │ 0 │ │ │\n", "│ 5 │ x5 │ 7.4 │ 1.2 │ │ │ 0 │ │ │\n", "│ 6 │ x6 │ 8.4 │ 1.3 │ │ │ 0 │ │ │\n", "│ 7 │ x7 │ 6.4 │ 1.1 │ │ │ 0 │ │ │\n", "│ 8 │ x8 │ 9.2 │ 1.7 │ │ │ 0 │ │ │\n", "│ 9 │ x9 │ 4.8 │ 2.3 │ │ │ 0 │ │ │\n", "│ 10│ x10 │ 7.0 │ 1.5 │ │ │ 0 │ │ │\n", "│ 11│ x11 │ 5.5 │ 1.0 │ │ │ 0 │ │ │\n", "│ 12│ x12 │ 5.1 │ 0.9 │ │ │ 0 │ │ │\n", "│ 13│ x13 │ 4.6 │ 0.9 │ │ │ 0 │ │ │\n", "│ 14│ x14 │ 4.7 │ 0.9 │ │ │ 0 │ │ │\n", "│ 15│ x15 │ 4.3 │ 0.8 │ │ │ 0 │ │ │\n", "│ 16│ x16 │ 3.2 │ 0.7 │ │ │ 0 │ │ │\n", "│ 17│ x17 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 18│ x18 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 19│ x19 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 20│ x20 │ 0.0 │ 0.5 │ │ │ 0 │ │ │\n", "│ 21│ x21 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 22│ x22 │ 2.1 │ 1.5 │ │ │ 0 │ │ │\n", "│ 23│ x23 │ 19 │ 4 │ │ │ 0 │ │ │\n", "│ 24│ x24 │ 54 │ 7 │ │ │ 0 │ │ │\n", "│ 25│ x25 │ 23 │ 4 │ │ │ 0 │ │ │\n", "│ 26│ x26 │ 1.1 │ 1.0 │ │ │ 0 │ │ │\n", "│ 27│ x27 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 28│ x28 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 29│ x29 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 30│ x30 │ 0.0 │ 0.4 │ │ │ 0 │ │ │\n", "│ 31│ x31 │ 0.0 │ 0.5 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌─────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\n", "│ │ x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 │\n", "├─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤\n", "│ x0 │ 2.24e+03 -1.45e+03 -14.6 -13.8 -14.6 -12 -13.6 -5.25 23.6 68.9 23.2 -6.29 -8.3 -7.42 -7.59 -7.06 -5.12 2.82e-08 -0 -1.04e-06 -2.67e-06 -0 -5.71 -26.2 29.8 5.32 -3.16 -0 -0 -0 -0 -2.52e-05 │\n", "│ x1 │ -1.45e+03 1.64e+03 14.6 13.8 14.6 12 13.6 5.25 -23.6 -68.9 -23.2 6.29 8.3 7.42 7.59 7.06 5.12 -2.72e-08 0 1.03e-06 2.66e-06 0 5.71 26.2 -29.8 -5.32 3.16 0 0 0 0 2.52e-05 │\n", "│ x2 │ -14.6 14.6 1.88 0.842 0.895 0.734 0.831 0.584 0.525 -0.302 0.345 0.522 0.507 0.453 0.464 0.432 0.313 2.37e-09 0 9.87e-09 2.43e-08 0 0.0578 0.264 -0.3 -0.0538 0.0319 0 0 0 0 2.62e-07 │\n", "│ x3 │ -13.8 13.8 0.842 1.71 0.842 0.69 0.781 0.549 0.494 -0.284 0.324 0.491 0.477 0.426 0.436 0.406 0.294 -1.08e-10 -0 8.99e-09 2.28e-08 0 0.0543 0.248 -0.282 -0.0505 0.03 0 0 0 0 2.48e-07 │\n", "│ x4 │ -14.6 14.6 0.895 0.842 1.88 0.734 0.831 0.584 0.525 -0.302 0.345 0.522 0.507 0.453 0.464 0.432 0.313 -1.07e-10 0 -7.03e-08 2.45e-08 0 0.0578 0.264 -0.3 -0.0537 0.0319 0 0 0 0 2.62e-07 │\n", "│ x5 │ -12 12 0.734 0.69 0.734 1.4 0.681 0.478 0.431 -0.248 0.283 0.428 0.416 0.372 0.38 0.354 0.256 -9.87e-11 0 7.25e-09 -1.65e-07 0 0.0473 0.216 -0.246 -0.0441 0.0262 0 0 0 0 2.16e-07 │\n", "│ x6 │ -13.6 13.6 0.831 0.781 0.831 0.681 1.68 0.541 0.487 -0.28 0.32 0.484 0.471 0.421 0.431 0.401 0.29 -1.17e-10 0 8.8e-09 2.29e-08 -0 0.0536 0.245 -0.278 -0.0499 0.0296 0 0 0 0 2.43e-07 │\n", "│ x7 │ -5.25 5.25 0.584 0.549 0.584 0.478 0.541 1.23 0.421 -0.0401 0.295 0.346 0.331 0.295 0.302 0.281 0.204 -1.31e-11 0 5.87e-09 1.39e-08 0 -0.424 0.207 0.116 0.0813 0.0193 0 0 0 0 1.4e-07 │\n", "│ x8 │ 23.6 -23.6 0.525 0.494 0.525 0.431 0.487 0.421 2.99 0.999 0.732 0.348 0.298 0.266 0.272 0.253 0.184 4.8e-11 0 1.44e-09 -1.43e-09 0 0.0195 -3.03 2.16 0.842 0.00754 0 0 0 0 -7.48e-08 │\n", "│ x9 │ 68.9 -68.9 -0.302 -0.284 -0.302 -0.248 -0.28 -0.0401 0.999 5.1 0.934 -0.0941 -0.171 -0.153 -0.157 -0.146 -0.106 1.73e-10 0 -1.09e-08 -3.64e-08 0 -0.048 0.432 -2.1 1.75 -0.0329 0 0 0 0 -5.36e-07 │\n", "│ x10 │ 23.2 -23.2 0.345 0.324 0.345 0.283 0.32 0.295 0.732 0.934 2.33 0.238 0.195 0.175 0.179 0.166 0.12 1.13e-12 0 4.71e-11 -4.1e-09 0 0.00938 0.336 1.97 -2.32 0.0023 0 0 0 0 -1.01e-07 │\n", "│ x11 │ -6.29 6.29 0.522 0.491 0.522 0.428 0.484 0.346 0.348 -0.0941 0.238 0.984 0.296 0.264 0.271 0.252 0.182 -9.79e-11 0 5.47e-09 1.33e-08 0 0.0327 0.172 -0.0118 0.0295 -0.223 0 0 0 0 1.37e-07 │\n", "│ x12 │ -8.3 8.3 0.507 0.477 0.507 0.416 0.471 0.331 0.298 -0.171 0.195 0.296 0.843 0.257 0.263 0.245 0.177 -7.11e-11 0 4.86e-09 1.39e-08 0 0.0327 0.149 -0.17 -0.0305 0.0181 -0 0 0 0 1.5e-07 │\n", "│ x13 │ -7.42 7.42 0.453 0.426 0.453 0.372 0.421 0.295 0.266 -0.153 0.175 0.264 0.257 0.726 0.235 0.219 0.158 -6.73e-11 0 4.78e-09 1.24e-08 0 0.0292 0.133 -0.152 -0.0272 0.0162 0 -0 0 0 1.34e-07 │\n", "│ x14 │ -7.59 7.59 0.464 0.436 0.464 0.38 0.431 0.302 0.272 -0.157 0.179 0.271 0.263 0.235 0.749 0.224 0.162 -5.43e-11 0 4.48e-09 1.29e-08 0 0.0299 0.137 -0.155 -0.0279 0.0165 0 0 -0 0 1.37e-07 │\n", "│ x15 │ -7.06 7.06 0.432 0.406 0.432 0.354 0.401 0.281 0.253 -0.146 0.166 0.252 0.245 0.219 0.224 0.681 0.151 -5.74e-11 0 4.48e-09 1.21e-08 0 0.0278 0.127 -0.145 -0.0259 0.0154 0 0 0 -0 1.27e-07 │\n", "│ x16 │ -5.12 5.12 0.313 0.294 0.313 0.256 0.29 0.204 0.184 -0.106 0.12 0.182 0.177 0.158 0.162 0.151 0.452 -7.57e-11 0 3.32e-09 9.14e-09 0 0.0202 0.0921 -0.105 -0.0188 0.0111 0 0 0 0 -1.25e-06 │\n", "│ x17 │ 2.82e-08 -2.72e-08 2.37e-09 -1.08e-10 -1.07e-10 -9.87e-11 -1.17e-10 -1.31e-11 4.8e-11 1.73e-10 1.13e-12 -9.79e-11 -7.11e-11 -6.73e-11 -5.43e-11 -5.74e-11 -7.57e-11 4.96e-09 0 -1.06e-15 4.24e-14 -0 -6.61e-11 3.35e-10 8.66e-10 4.04e-10 6.92e-12 0 0 -0 -0 -1.87e-13 │\n", "│ x18 │ -0 0 0 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 -0 0 0 0 0 0 -0 -0 0 -0 0 │\n", "│ x19 │ -1.04e-06 1.03e-06 9.87e-09 8.99e-09 -7.03e-08 7.25e-09 8.8e-09 5.87e-09 1.44e-09 -1.09e-08 4.71e-11 5.47e-09 4.86e-09 4.78e-09 4.48e-09 4.48e-09 3.32e-09 -1.06e-15 0 5.45e-07 -1.29e-13 0 6.39e-10 2.48e-09 -1.99e-08 -6.18e-09 -2.06e-10 0 0 0 0 -8.77e-13 │\n", "│ x20 │ -2.67e-06 2.66e-06 2.43e-08 2.28e-08 2.45e-08 -1.65e-07 2.29e-08 1.39e-08 -1.43e-09 -3.64e-08 -4.1e-09 1.33e-08 1.39e-08 1.24e-08 1.29e-08 1.21e-08 9.14e-09 4.24e-14 -0 -1.29e-13 6.59e-07 -0 1.99e-09 5.74e-10 -7.53e-08 -2.41e-08 1.21e-09 -0 -0 -0 -0 6.71e-13 │\n", "│ x21 │ -0 0 0 0 0 0 -0 0 0 0 0 0 0 0 0 0 0 -0 -0 0 -0 0 0 0 0 0 0 -0 -0 -0 -0 0 │\n", "│ x22 │ -5.71 5.71 0.0578 0.0543 0.0578 0.0473 0.0536 -0.424 0.0195 -0.048 0.00938 0.0327 0.0327 0.0292 0.0299 0.0278 0.0202 -6.61e-11 0 6.39e-10 1.99e-09 0 2.19 0.0106 -0.0762 -0.0247 0.00233 0 0 0 0 2.55e-08 │\n", "│ x23 │ -26.2 26.2 0.264 0.248 0.264 0.216 0.245 0.207 -3.03 0.432 0.336 0.172 0.149 0.133 0.137 0.127 0.0921 3.35e-10 0 2.48e-09 5.74e-10 0 0.0106 17.8 0.947 0.371 0.00444 0 0 0 0 -1.79e-08 │\n", "│ x24 │ 29.8 -29.8 -0.3 -0.282 -0.3 -0.246 -0.278 0.116 2.16 -2.1 1.97 -0.0118 -0.17 -0.152 -0.155 -0.145 -0.105 8.66e-10 0 -1.99e-08 -7.53e-08 0 -0.0762 0.947 49.8 3.46 -0.0548 0 0 0 0 -9.96e-07 │\n", "│ x25 │ 5.32 -5.32 -0.0538 -0.0505 -0.0537 -0.0441 -0.0499 0.0813 0.842 1.75 -2.32 0.0295 -0.0305 -0.0272 -0.0279 -0.0259 -0.0188 4.04e-10 0 -6.18e-09 -2.41e-08 0 -0.0247 0.371 3.46 18.9 -0.0184 0 0 0 0 -3.5e-07 │\n", "│ x26 │ -3.16 3.16 0.0319 0.03 0.0319 0.0262 0.0296 0.0193 0.00754 -0.0329 0.0023 -0.223 0.0181 0.0162 0.0165 0.0154 0.0111 6.92e-12 0 -2.06e-10 1.21e-09 0 0.00233 0.00444 -0.0548 -0.0184 1.14 0 0 0 0 1.54e-08 │\n", "│ x27 │ -0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 0 0 0 -0 0 -0 -0 0 0 0 0 0 0 -0 -0 -0 0 │\n", "│ x28 │ -0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 0 0 -0 0 -0 -0 0 0 0 0 0 -0 0 -0 -0 0 │\n", "│ x29 │ -0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 0 -0 0 0 -0 -0 0 0 0 0 0 -0 -0 0 -0 0 │\n", "│ x30 │ -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0 0 -0 -0 0 -0 -0 0 0 0 0 0 -0 -0 -0 0 0 │\n", "│ x31 │ -2.52e-05 2.52e-05 2.62e-07 2.48e-07 2.62e-07 2.16e-07 2.43e-07 1.4e-07 -7.48e-08 -5.36e-07 -1.01e-07 1.37e-07 1.5e-07 1.34e-07 1.37e-07 1.27e-07 -1.25e-06 -1.87e-13 0 -8.77e-13 6.71e-13 0 2.55e-08 -1.79e-08 -9.96e-07 -3.5e-07 1.54e-08 0 0 0 0 5.19e-06 │\n", "└─────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class BB:\n", " def __init__(self, xe, n, t):\n", " self.xe = xe\n", " self.data = n, t\n", "\n", " def _pred(self, par):\n", " bins = len(self.xe) - 1\n", " yields = par[:2]\n", " nuisances = np.array(par[2:])\n", " b = nuisances[:bins]\n", " s = nuisances[bins:]\n", " mu = 0\n", " for y, c in zip(yields, (b, s)):\n", " mu += y * np.array(c) / np.sum(c)\n", " return mu, b, s\n", "\n", " def __call__(self, par):\n", " n, t = self.data\n", " mu, b, s = self._pred(par)\n", " r = poisson_chi2(n, mu) + poisson_chi2(t[0], b) + poisson_chi2(t[1], s)\n", " return r\n", "\n", " @property\n", " def ndata(self):\n", " n, t = self.data\n", " return np.prod(n.shape) + np.prod(t.shape)\n", "\n", " def visualize(self, args):\n", " n, t = self.data\n", " ne = n ** 0.5\n", " xe = self.xe\n", " cx = 0.5 * (xe[1:] + xe[:-1])\n", " plt.errorbar(cx, n, ne, fmt=\"ok\")\n", " mu = 0\n", " mu_var = 0\n", " for y, c in zip(args[:2], t):\n", " f = 1 / np.sum(c)\n", " mu += c * y * f\n", " mu_var += c * (f * y) ** 2\n", " mu_err = mu_var ** 0.5\n", " plt.stairs(mu + mu_err, xe, baseline=mu - mu_err, fill=True, color=\"C0\")\n", " # plt.stairs(mu, xe, color=\"C0\")\n", "\n", "m1 = Minuit(BB(xe, n, t), np.concatenate([truth, t[0], t[1]]))\n", "m1.limits = (0, None)\n", "m1.migrad(ncall=100000)\n", "m1.hesse()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result of this fit is comparable to the bootstrap method for this example, but the chi2/ndof is now reasonable and the uncertainties are correct without further work. This method should perform better than the bootstrap method, if the count per bin in the templates is small.\n", "\n", "Another advantage is of this technique is that one can profile over the likelihood to obtain a 2D confidence regions, which is not possible with the bootstrap technique." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m1.draw_mncontour(\"x0\", \"x1\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before moving on, we briefly explore a possible refinement of the previous method, which is to hide the nuisance parameters from Minuit with a nested fit. It turns out that this technique is not an improvement, but it is useful to show that explicitly.\n", "\n", "The idea is to construct an outer cost function, which only has the yields as parameters. Inside the outer cost function, the best nuisance parameters are found for the current yields with an inner cost function. Technically, this is achieved by calling a minimizer on the inner cost function at every call to the outer cost function.\n", "\n", "Technical detail: It is important here to adjust Minuit's expectation of how accurate the cost function is computed. Usually, Minuit performs its internal calculations under the assumption that the cost function is accurate to machine precision. This is usually not the case when a minimizer is used internally to optimize the inner function. We perform the internal minimization with SciPy, which allows us to set the tolerance. We set it here to 1e-8, which is sufficient for this problem and saves a bit of time on the internal minimisation. We then instruct Minuit to expect only this precision." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 18.27 (chi2/ndof = 1.4) Nfcn = 60
EDM = 2.94e-08 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 800 50 0
1 x1 190 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 2.24e+03 -1.45e+03 (-0.756)
x1 -1.45e+03 (-0.756) 1.64e+03
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 18.27 (chi2/ndof = 1.4) │ Nfcn = 60 │\n", "│ EDM = 2.94e-08 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 800 │ 50 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 190 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬─────────────────────┐\n", "│ │ x0 x1 │\n", "├────┼─────────────────────┤\n", "│ x0 │ 2.24e+03 -1.45e+03 │\n", "│ x1 │ -1.45e+03 1.64e+03 │\n", "└────┴─────────────────────┘" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "precision = 1e-8\n", "\n", "def cost(yields):\n", " bins = len(n)\n", "\n", " def inner(nuisance):\n", " b = nuisance[:bins]\n", " s = nuisance[bins:]\n", " mu = 0\n", " for y, c in zip(yields, (b, s)):\n", " mu += y * c / np.sum(c)\n", " r = poisson_chi2(n, mu) + poisson_chi2(t[0], b) + poisson_chi2(t[1], s)\n", " return r\n", "\n", " bounds = np.zeros((2 * bins, 2))\n", " bounds[:, 1] = np.inf\n", " r = minimize(inner, np.ravel(t), bounds=bounds, tol=precision)\n", " assert r.success\n", " return r.fun\n", "\n", "cost.errordef = Minuit.LEAST_SQUARES\n", "cost.ndata = np.prod(n.shape)\n", "\n", "m2 = Minuit(cost, truth)\n", "m2.precision = precision\n", "m2.limits = (0, None)\n", "m2.migrad()\n", "m2.hesse()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We obtain the exact same result as expected, but the runtime is much longer (more than a factor 10), which disfavors this technique compared to the straight-forward fit. The minimization is not as efficient, because Minuit cannot exploit correlations between the internal and the external parameters that allow it to converge it faster when it sees all parameters at once." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Template fits\n", "\n", "The implementation described by [Barlow and Beeston, Comput.Phys.Commun. 77 (1993) 219-228](https://doi.org/10.1016/0010-4655(93)90005-W) solves the problem similarly to the nested fit described above, but the solution to the inner problem is found with an efficient algorithm.\n", "\n", "The Barlow-Beeston approach still requires numerically solving a non-linear equation per bin. Several authors tried to replace the stop with approximations.\n", "- [Conway, PHYSTAT 2011, https://arxiv.org/abs/1103.0354](https://doi.org/10.48550/arXiv.1103.0354) uses an approximation in which the optimal nuisance parameters can be found by bin-by-bin by solving a quadratic equation which has only one allowed solution.\n", "- [C.A. Argüelles, A. Schneider, T. Yuan, JHEP 06 (2019) 030](https://doi.org/10.48550/arXiv.1103.0354) use a Bayesian treatment in which the uncertainty from the finite size of the simulation sample is modeled with a prior (which is itself a posterior conditioned on the simulation result). A closed formula for the likelihood can be derived.\n", "- [H. Dembinski, A. Abdelmotteleb, Eur.Phys.J.C 82 (2022) 11, 1043](https://doi.org/10.1140/epjc/s10052-022-11019-z) derived an approximation similar to Conway, but which treats data and simulation symmetrically.\n", "\n", "All three methods are implemented in the built-in `Template` cost function (see documentation for details)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 11.52 (chi2/ndof = 0.9) Nfcn = 48
EDM = 6.05e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 0.86e3 0.11e3 0
1 x1 190 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 1.16e+04 -1.56e+03 (-0.330)
x1 -1.56e+03 (-0.330) 1.94e+03
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 11.52 (chi2/ndof = 0.9) │ Nfcn = 48 │\n", "│ EDM = 6.05e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 0.86e3 │ 0.11e3 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 190 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬─────────────────────┐\n", "│ │ x0 x1 │\n", "├────┼─────────────────────┤\n", "│ x0 │ 1.16e+04 -1.56e+03 │\n", "│ x1 │ -1.56e+03 1.94e+03 │\n", "└────┴─────────────────────┘" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = Template(n, xe, t, method=\"jsc\") # Conway\n", "m3 = Minuit(c, *truth)\n", "m3.limits = (0, None)\n", "m3.migrad()\n", "m3.hesse()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 61.24 Nfcn = 43
EDM = 1.03e-05 (Goal: 0.0001)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 660 70 0
1 x1 210 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 5.25e+03 -838 (-0.290)
x1 -838 (-0.290) 1.59e+03
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 61.24 │ Nfcn = 43 │\n", "│ EDM = 1.03e-05 (Goal: 0.0001) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 660 │ 70 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 210 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬───────────────────┐\n", "│ │ x0 x1 │\n", "├────┼───────────────────┤\n", "│ x0 │ 5.25e+03 -838 │\n", "│ x1 │ -838 1.59e+03 │\n", "└────┴───────────────────┘" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = Template(n, xe, t, method=\"asy\") # Argüelles, Schneider, Yuan\n", "m4 = Minuit(c, *truth)\n", "m4.limits = (0, None)\n", "m4.migrad()\n", "m4.hesse()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 11.42 (chi2/ndof = 0.9) Nfcn = 47
EDM = 1.85e-06 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 760 90 0
1 x1 190 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 8.08e+03 -1.26e+03 (-0.329)
x1 -1.26e+03 (-0.329) 1.81e+03
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 11.42 (chi2/ndof = 0.9) │ Nfcn = 47 │\n", "│ EDM = 1.85e-06 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 760 │ 90 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 190 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬─────────────────────┐\n", "│ │ x0 x1 │\n", "├────┼─────────────────────┤\n", "│ x0 │ 8.08e+03 -1.26e+03 │\n", "│ x1 │ -1.26e+03 1.81e+03 │\n", "└────┴─────────────────────┘" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = Template(n, xe, t, method=\"da\") # Dembinski, Abdelmotteleb\n", "m5 = Minuit(c, *truth)\n", "m5.limits = (0, None)\n", "m5.migrad()\n", "m5.hesse()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "full fit\n", " x0 796 +- 47\n", " x1 187 +- 40\n", " correlation -0.76\n", "T(JSC)\n", " x0 858 +- 108\n", " x1 185 +- 44\n", " correlation -0.33\n", "T(ASY)\n", " x0 660 +- 72\n", " x1 205 +- 40\n", " correlation -0.29\n", "T(DA)\n", " x0 762 +- 90\n", " x1 194 +- 43\n", " correlation -0.33\n" ] } ], "source": [ "for title, m in zip((\"full fit\", \"T(JSC)\", \"T(ASY)\", \"T(DA)\"), (m1, m3, m4, m5)):\n", " print(title)\n", " cov = m.covariance\n", " for label, p, e in zip((\"x0\", \"x1\"), m.values, np.diag(cov) ** 0.5):\n", " print(f\" {label} {p:.0f} +- {e:.0f}\")\n", " print(f\" correlation {cov[0, 1] / (cov[0, 0] * cov[1, 1]) ** 0.5:.2f}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The yields found by the approximate Template methods (T) differ from those found with the exact Barlow-Beeston likelihood (BB) method, because the likelihoods are different. In this particular case, the uncertainty for the signal estimated by the Template methods is larger, but this not the case in general.\n", "\n", "The difference shows up in particular in the 68 % confidence regions." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c1 = m1.mncontour(\"x0\", \"x1\")\n", "c3 = m3.mncontour(\"x0\", \"x1\")\n", "c4 = m4.mncontour(\"x0\", \"x1\")\n", "c5 = m5.mncontour(\"x0\", \"x1\")\n", "plt.plot(c1[:,0], c1[:, 1], label=\"BB\")\n", "plt.plot(c3[:,0], c3[:, 1], label=\"T(JSC)\")\n", "plt.plot(c4[:,0], c4[:, 1], label=\"T(ASY)\")\n", "plt.plot(c5[:,0], c4[:, 1], label=\"T(DA)\")\n", "plt.xlabel(\"background yield\")\n", "plt.ylabel(\"signal yield\")\n", "plt.legend();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "In this particular example, the BB method produces the smallest uncertainty for the background yield. The uncertainty for the signal yield is similar for all methods.\n", "\n", "In general, the approximate methods perform well, the `T(DA)` method in particular is very close to the performance of the exact BB likelihood. The `T(DA)` method also has the advantage that it can be used with weighted data and/or weighted simulated samples.\n", "\n", "For an in-depth comparison of the four methods, see [H. Dembinski, A. Abdelmotteleb, Eur.Phys.J.C 82 (2022) 11, 1043](https://doi.org/10.1140/epjc/s10052-022-11019-z)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.12 ('venv': venv)", "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.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)]" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/template_gof.ipynb0000644000000000000000000066575114332717401016512 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Template fits: comparing two chi-square distributed test statistics\n", "\n", "The builtin binned cost functions in `iminuit.cost` provide a method `pulls` which returns the pull distribution for a given data set. The sum of pulls squared is asymptotically chi-square distributed, but as is explained in the documentation, it is better to use the minimum value of the cost function instead of the pulls to compute this test statistic. The reason is that the cost function has been designed so that the minimum is chi-square distributed and it approaches the asymptotic limit faster than a simple sum of pulls squared.\n", "\n", "We demonstrate this in this example for a Template fit. We generate random samples from a mixed model (gaussian peak over exponential background), which is fitted many times. The distribution of the cost function minimum and alternatively the sum of pulls squared is compared with a chi-square distribution by computing p-values based on the expected distribution. If the test statistic follows the chi-square distribution, the distribution of p-values must be uniform." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "from iminuit import cost, Minuit\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "from scipy.stats import chi2\n", "from IPython.display import display" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 13.87 (χ²/ndof = 0.8) Nfcn = 101
EDM = 2.91e-08 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 1.01e3 0.05e3 0
1 x1 870 50 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 2.49e+03 -0.5e3 (-0.196)
x1 -0.5e3 (-0.196) 2.21e+03
\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-04-27T12:51:54.476322\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.7.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 13.87 (χ²/ndof = 0.8) │ Nfcn = 101 │\n", "│ EDM = 2.91e-08 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 1.01e3 │ 0.05e3 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 870 │ 50 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬───────────────────┐\n", "│ │ x0 x1 │\n", "├────┼───────────────────┤\n", "│ x0 │ 2.49e+03 -0.5e3 │\n", "│ x1 │ -0.5e3 2.21e+03 │\n", "└────┴───────────────────┘" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 201.8 (χ²/ndof = 1.0) Nfcn = 100
EDM = 2.37e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0 1.02e3 0.05e3 0
1 x1 810 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0 x1
x0 2.41e+03 -0.3e3 (-0.161)
x1 -0.3e3 (-0.161) 1.92e+03
\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-04-27T12:51:54.631153\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.7.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 201.8 (χ²/ndof = 1.0) │ Nfcn = 100 │\n", "│ EDM = 2.37e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0 │ 1.02e3 │ 0.05e3 │ │ │ 0 │ │ │\n", "│ 1 │ x1 │ 810 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌────┬───────────────────┐\n", "│ │ x0 x1 │\n", "├────┼───────────────────┤\n", "│ x0 │ 2.41e+03 -0.3e3 │\n", "│ x1 │ -0.3e3 1.92e+03 │\n", "└────┴───────────────────┘" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "xr = (0, 2) # xrange\n", "rng = np.random.default_rng(1)\n", "\n", "nmc = 1000\n", "trials = 1000\n", "\n", "data = {}\n", "data2 = {}\n", "\n", "first = True\n", "for trial in range(trials):\n", " for bins in (20, 200,):\n", " xdata = rng.normal(1, 0.1, size=1000)\n", " ydata = rng.exponential(size=len(xdata))\n", " xmix = np.append(xdata, ydata)\n", " xmix = xmix[(xr[0] < xmix) & (xmix < xr[1])]\n", "\n", " n, xe = np.histogram(xmix, bins=bins, range=xr)\n", "\n", " x = rng.normal(1, 0.1, size=nmc)\n", " y = rng.exponential(size=nmc)\n", " t = [\n", " np.histogram(x, bins=bins, range=xr)[0],\n", " np.histogram(y, bins=bins, range=xr)[0],\n", " ]\n", " c = cost.Template(n, xe, t)\n", " m = Minuit(c, 1, 1)\n", " m.migrad()\n", " assert m.valid\n", " assert m.accurate\n", " data.setdefault(bins, []).append(m.fmin.fval)\n", " data2.setdefault(bins, []).append(np.nansum(c.pulls(m.values) ** 2))\n", " # display one example fit\n", " if first:\n", " display(m)\n", " first = False\n", "\n", "for key in tuple(data):\n", " val = data[key]\n", " data[key] = np.array(val)\n", " val = data2[key]\n", " data2[key] = np.array(val)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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1a1bV+3bt5ckrVq1SlevXtWKFStUpUoVSdLcuXPVo0cPvfPOO6pUqZJycnL0q1/9SnXr1pUkNWzY0N5/1apV5eXlddtTJlWrVlVgYKCkOzu10q1bN7300kuSpFGjRmnmzJnaunWr6tevr1WrVum7777Tl19+qaCgIEnSAw884FR9xY1BcHCwJKl69eqaO3euPD091aBBA3Xv3l1JSUkElDvk9Cmezz//XD169FBYWNgtU2AFBQUaNWqUmjRpoipVqigsLEx9+/bV2bNnHfo4f/68+vTpI39/fwUGBurFF190mCoDAJSNtLQ05eXlqXPnzj+5v1mzZvYPZklq166dCgsLdeTIEQUFBal///6KiYlRjx499Ic//OGOZkHuRtOmTe1/ttlsCgkJ0blz5yRJqampatGihT2c3InixuCGxo0by9PT0/48NDTUXgec53RAuXz5spo1a6Z58+bdsu/KlSvat2+fxo4dq3379mnt2rU6cuSInnjiCYd2ffr00ddff60tW7boo48+0ueff674+Pg7PwoAwB3x8/O76z6WLl2q5ORktW3bVu+//75+8YtfaNeuXXfdr4eHhyzLcthW1JqOSpUqOTy32WwqLCyU5JrjK6nb1QHnOR1QunbtqsmTJ+vJJ5+8ZV9AQIC2bNmiXr16qX79+nrkkUc0d+5cpaSk6PTp05J+SKKbN2/Wn/70J7Vp00bt27fXnDlztHr16ltmWgAApatevXry8/NTUlJSkfsbNmyoAwcO6PLly/ZtX3zxhTw8PFS/fn37thYtWmjMmDHauXOnHnzwQa1atUqS5O3trevXr99RbbVq1XKYjbl+/bq++uorp/po2rSpUlNTdf78+SL3l6S+ko4BXKvUr+LJycmRzWazn19MTk5WYGCgWrVqZW8THR0tDw8P7d69u8g+8vLylJub6/AAANw9X19fjRo1SiNHjtSKFSt0/Phx7dq1S0uWLJH0w4y3r6+v+vXrp6+++kpbt27V0KFD9fzzzys4OFjp6ekaM2aMkpOTderUKX366ac6evSofR1KRESE0tPTlZqaqu+//155eXklrq1Tp07auHGjNm7cqMOHD2vIkCHKzs526vieeeYZhYSEKDY2Vl988YVOnDih//3f/1VycnKJ6ytuDFA6SnWR7NWrVzVq1Cg988wz8vf3lyRlZmaqdu3ajkV4eSkoKEiZ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nHIqDQQUAKjAPD09tWXLFn388cdq1KiR5syZo/r16ys9PV2S5OHhccupi4KCglv6qVSpkv3PN9ZGFLWtsLDQ5cfgrJIe008pbsxu1rVrV506dUrDhw/X2bNn1blzZ6dO45Sk3ldffVXr1q3TlClT9M9//lOpqalq0qSJ8vPzHdpVqVLF4fmlS5c0ePBgpaam2h8HDhzQ0aNHVbdu3RLXWBoIKABQwdlsNrVr104TJ07U/v375e3trXXr1kn64XRKRkaGve3169f11VdfubyGhg0b6syZMzpz5ox926FDh5Sdna1GjRo53d+uXbvsf7527ZpSUlLspyVq1aqlixcvOiwEvbEgtaRuN2ZFqVWrlvr166e//OUvmjVrlhYtWiTph+P+17/+patXrxZZe0nr/eKLL9S/f389+eSTatKkiUJCQopcRHyzhx56SIcOHdIDDzxwy8Pb21sNGjSwj98NR44cUXZ2drF93y0CCgBUYLt379aUKVO0d+9enT59WmvXrtV3331n/zDv1KmTNm7cqI0bN+rw4cMaMmRIqXw4RUdHq0mTJurTp4/27dunPXv2qG/fvurQocMtpyVKYt68eVq3bp0OHz6shIQEXbhwQQMGDJAktWnTRpUrV9brr7+u48ePa9WqVVq2bFmJ+y5uzG42btw4ffDBBzp27Ji+/vprffTRR/a2zz77rGw2mwYNGqRDhw5p06ZNevfddx1eX5J669Wrp7Vr19pnQJ599tkSzVaNGjVKO3fuVGJiolJTU3X06FF98MEH9nUr9evXV5cuXTR48GDt3r1bKSkpGjhwoPz8/Eo8XneKRbIAUNoMvrOrv7+/Pv/8c82aNUu5ubm677779N5776lr166SpAEDBujAgQPq27evvLy8NHz4cD322GMur8Nms+mDDz7Q0KFD9ctf/lIeHh7q0qWL5syZc0f9TZs2TdOmTVNqaqoeeOABbdiwwX7lTFBQkP7yl7/otdde0+LFi9W5c2dNmDDBftltcYobs5t5e3trzJgxOnnypPz8/PToo49q9erVkqSqVavqww8/1G9/+1u1aNFCjRo10jvvvGNf4FvSemfMmKEBAwaobdu2qlmzpkaNGqXc3Nxij6Vp06bavn273njjDT366KOyLEt169a1Xz0lSUuXLtXAgQPVoUMHBQcHa/LkyRo7dmyJxupu2CxnryUzQG5u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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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YNWtW1cdO3bUnj17rlufzWbTunXryt0Hw4cP14ABA/TWW28pLCxMDRo0UHx8vC5fvuzKXwV+hhGUsmyf7e4KXNd9irsrAFBDTZkyRUuXLtX8+fP10EMPKTMzU0ePHpUkXbx4UdHR0YqKitK+ffuUk5OjUaNGKSEhQYmJiSoqKtKAAQP0zDPP6K9//asKCwu1d+9e2Ww2DR48WF9//bU2b96sv/3tb5KkwMDAq47/0ksvKSIiQk8//bQyMzNdrv+Pf/yjZs2apd/97ndavXq1xowZo65du6ply5a6cOGCunbtql/84hf69NNPFRoaqgMHDqikpKTc9d2oD0pt375dYWFh2r59u7799lsNHjxY7du31zPPPOPyOYGAAgC12vnz5/WnP/1J77zzjuLi4iRJzZs310MPPSRJWrlypS5duqT3339fdevWlSS988476tevn37/+9+rTp06ysvL06OPPqrmzZtLklq3bu3Yf0BAgLy8vK57ySQgIEBBQUGSKnZppW/fvnr++eclSZMnT9b8+fO1fft2tWzZUitXrtSZM2e0b98+BQcHS5LuvPNOl+q7UR+EhIRIkurXr6933nlHnp6eatWqlWJiYrRt2zYCSgVxiQcAarG0tDQVFBTokUceueb6du3aOT6YJalLly4qKSnRsWPHFBwcrOHDhys6Olr9+vXTn/70pwqNgtyMtm3bOv5ss9kUGhqqnJwcSVJKSoruvfdeRzipiBv1Qam7775bnp6ejvdhYWGOOuA6AgoA1GJ+fn43vY/ly5crKSlJDz74oD766CPddddd2r17903v18PDQ5ZlOS0ra05HnTp1nN7bbDaVlJRIqpzzK6/r1QHXEVAAoBZr0aKF/Pz8tG3btjLXt27dWocOHdLFixcdy7766it5eHioZcuWjmX33nuvpkyZol27dumee+7RypUrJUne3t4qLi6uUG2NGjVyGo0pLi7W119/7dI+2rZtq5SUFP3www9lri9PfeXtA1QuAgoA1GK+vr6aPHmyJk2apPfff1/Hjx/X7t27tWzZMknSkCFD5Ovrq7i4OH399dfavn27xo4dq6FDhyokJETp6emaMmWKkpKS9N1332nLli365ptvHPNQIiIilJ6erpSUFJ09e1YFBQXlrq1Hjx7asGGDNmzYoKNHj2rMmDHKzc116fyefPJJhYaGasCAAfrqq690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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "for bins in data:\n", " plt.figure()\n", " plt.title(f\"bins = {bins}\")\n", " plt.hist(chi2(bins-2).cdf(data[bins]), bins=10, range=(0, 1), label=\"cost function\")\n", " plt.hist(chi2(bins-2).cdf(data2[bins]), bins=10, range=(0, 1), alpha=0.5, label=\"sum of pulls squared\")\n", " plt.legend();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "When 20 bins are used, the number of counts per bin is large enough so that both test statistics are chi-square distributed. When 200 bins are used with samples of the same size, the density in some bins drops low enough so that we are not in the asymptotic limit and see deviations from the theoretical chi-square distribution. These deviations are larger for the sum of pulls squared." ] } ], "metadata": { "kernelspec": { "display_name": "py310", "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.10.9" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/notebooks/template_model_mix.ipynb0000644000000000000000000037560014332717401017703 0ustar00{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Fitting a mixture of templates and parametric models\n", "\n", "The class `iminuit.cost.Template` supports fitting a mixture of templates and parametric models. This is useful if some components have a simple shape like a Gaussian peak, while other components are complicated and need to be estimated from simulation or a control measurement.\n", "\n", "In this notebook, we demonstrate this usage. Our data consists of a Gaussian peak and exponential background. We fit the Gaussian peak with a parametric model and use a template to describe the exponential background." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "from iminuit import Minuit\n", "from iminuit.cost import Template, ExtendedBinnedNLL\n", "from numba_stats import norm, truncexpon\n", "import numpy as np\n", "from matplotlib import pyplot as plt" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We generate the toy data." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAp7klEQVR4nO3deXRUZZ7G8aeyB0gqbNm6gwSGVUGQJUagsSFtRpaGkW6lzSAIDT2QMEKOsjSb7MthE0QYFgE9YLrpaRhGaGwI2iiEgDEoCkSUINiaoGOTsHQSktz5w2O1RdKShKrUW8n3c06dQ269de/vvgTr8X3fe6/NsixLAAAABvHxdAEAAAC3I6AAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIzj5+kCaqK8vFxffPGFQkJCZLPZPF0OAACoAsuydO3aNUVHR8vH54fHSLwyoHzxxReKiYnxdBkAAKAGLl++rB//+Mc/2MYrA0pISIikb08wNDTUw9UAAICqKCwsVExMjON7/Id4ZUD5blonNDSUgAIAgJepyvIMFskCAADjEFAAAIBxCCgAAMA4XrkGBQBgnrKyMt26dcvTZcCDfH195efn55JbgBBQAAB37fr16/r8889lWZanS4GHNWjQQFFRUQoICLir/RBQAAB3paysTJ9//rkaNGig5s2bcwPNesqyLJWUlOirr75Sbm6u2rRpc8ebsf0QAgoA4K7cunVLlmWpefPmCg4O9nQ58KDg4GD5+/vrs88+U0lJiYKCgmq8r2pHmyNHjmjw4MGKjo6WzWbTnj17nN63LEuzZ89WVFSUgoODlZCQoPPnzzu1+eabb5SUlKTQ0FCFhYVpzJgxun79eo1PAgDgeYycQNJdjZo47ae6H7hx44buv/9+rVu3rtL3ly1bpjVr1mjDhg3KzMxUw4YNlZiYqKKiIkebpKQkffTRRzp48KBef/11HTlyROPGjav5WQAAgDql2gHl0Ucf1YIFC/Rv//ZvFd6zLEurV6/WzJkzNWTIEHXu3FmvvPKKvvjiC8dIy9mzZ3XgwAFt3rxZcXFx6t27t9auXau0tDR98cUXd31CAABUxcMPP6xJkya5bf+jRo3S0KFD3bZ/T7h48aJsNptOnTrl9mO5dA1Kbm6u8vLylJCQ4Nhmt9sVFxenjIwMDR8+XBkZGQoLC1P37t0dbRISEuTj46PMzMxKg09xcbGKi4sdPxcWFrqybACAOzxvr+XjFdTu8eBWLr1RW15eniQpIiLCaXtERITjvby8PIWHhzu97+fnpyZNmjja3G7x4sWy2+2OF08yBgDURyUlJZ4uodZ4xZ1kp0+froKCAsfr8uXLni4JAFAHlJaWKiUlRXa7Xc2aNdOsWbMc93J59dVX1b17d4WEhCgyMlJPPvmkrly54vT5jz76SIMGDVJoaKhCQkLUp08fffrpp5Ue6+TJk2revLmWLl3q2LZgwQKFh4crJCREv/71rzVt2jR16dLF8f5300QLFy5UdHS02rVrJ0k6ffq0+vXrp+DgYDVt2lTjxo1zutiksumroUOHatSoUY6fW7ZsqUWLFmn06NEKCQlRixYttHHjRqfPnDhxQl27dlVQUJC6d++u7OzsKvft3XJpQImMjJQk5efnO23Pz893vBcZGVnhL7i0tFTffPONo83tAgMDHU8u5gnGAABX2b59u/z8/HTixAm98MILWrlypTZv3izp28un58+fr/fff1979uzRxYsXnb7g//rXv+onP/mJAgMDdfjwYWVlZWn06NEqLS2tcJzDhw/rZz/7mRYuXKipU6dKknbs2KGFCxdq6dKlysrKUosWLbR+/foKn01PT1dOTo7jwpIbN24oMTFRjRs31smTJ7Vr1y4dOnRIKSkp1T7/FStWOILHhAkTNH78eOXk5Ej69uZ7gwYNUseOHZWVlaXnn39ezz77bLWPUVMuXYMSGxuryMhIpaenOxJgYWGhMjMzNX78eElSfHy8rl69qqysLHXr1k3St39x5eXliouLc2U5ALxUy2n7Kmy7uGSgBypBXRcTE6NVq1bJZrOpXbt2On36tFatWqWxY8dq9OjRjnatWrXSmjVr1KNHD12/fl2NGjXSunXrZLfblZaWJn9/f0lS27ZtKxxj9+7deuqpp7R582Y98cQTju1r167VmDFj9PTTT0uSZs+erT//+c8VbrvRsGFDbd682XFn1k2bNqmoqEivvPKKGjZsKEl68cUXNXjwYC1durTCMosfMmDAAE2YMEGSNHXqVK1atUpvvvmm2rVrp507d6q8vFxbtmxRUFCQ7r33Xn3++eeO73N3q/YIyvXr13Xq1CnHCt7c3FydOnVKly5dks1m06RJk7RgwQLt3btXp0+f1lNPPaXo6GjHSuYOHTroX//1XzV27FidOHFCR48eVUpKioYPH67o6GhXnhsAAD/owQcfdLp/S3x8vM6fP6+ysjJlZWVp8ODBatGihUJCQtS3b19J0qVLlyRJp06dUp8+fRzhpDKZmZn65S9/qVdffdUpnEhSTk6Oevbs6bTt9p8lqVOnTk63jT979qzuv/9+RziRpF69eqm8vNwx+lFVnTt3dvzZZrM5zXKcPXtWnTt3drrZWnx8fLX2fzeqHVDeffddde3aVV27dpUkpaamqmvXrpo9e7YkacqUKZo4caLGjRvnSJoHDhxwOsEdO3aoffv26t+/vwYMGKDevXtXmPcCAMBTioqKlJiYqNDQUO3YsUMnT57U7t27Jf1joWpV7prbunVrtW/fXi+//HKNH6T4/SBSVT4+PhWei1TZ8W8PVzabTeXl5dU+njtUO6A8/PDDsiyrwmvbtm2Svj25efPmKS8vT0VFRTp06FCFIa8mTZpo586dunbtmgoKCvTyyy+rUaNGLjkhAACqKjMz0+nn48ePq02bNjp37pz+7//+T0uWLFGfPn3Uvn37CusnO3furLfffvsHg0ezZs10+PBhffLJJ3r88ced2rZr104nT550an/7z5Xp0KGD3n//fd24ccOx7ejRo/Lx8XEsom3evLm+/PJLx/tlZWX68MMP77jv24/zwQcfON1o9fjx49Xax93wiqt4AABwh0uXLik1NVU5OTl67bXXtHbtWj3zzDNq0aKFAgICtHbtWl24cEF79+7V/PnznT6bkpKiwsJCDR8+XO+++67Onz+vV199tcI0S3h4uA4fPqxz587pV7/6lWMR7cSJE7VlyxZt375d58+f14IFC/TBBx/c8ZEBSUlJCgoK0siRI/Xhhx/qzTff1MSJEzVixAjH+pN+/fpp37592rdvn86dO6fx48fr6tWr1eqbJ598UjabTWPHjtWZM2e0f/9+LV++vFr7uBsEFABAvfXUU0/p73//u3r27Knk5GQ988wzGjdunJo3b65t27Zp165d6tixo5YsWVLhy7lp06Y6fPiwrl+/rr59+6pbt27atGlTpWtSIiMjdfjwYZ0+fVpJSUkqKytTUlKSpk+frmeffVYPPPCAcnNzNWrUqDs+YK9BgwZ644039M0336hHjx76xS9+of79++vFF190tBk9erRGjhypp556Sn379lWrVq3005/+tFp906hRI/3v//6vTp8+ra5du2rGjBlOl0i7m826fZLKCxQWFsput6ugoIBLjoE6iKt4vEtRUZFyc3MVGxt7V0+vhfSzn/1MkZGRevXVVz1dSo390O9Ddb6/XXqZMQAAqJqbN29qw4YNSkxMlK+vr1577TUdOnRIBw8e9HRpRiCgAADgATabTfv379fChQtVVFSkdu3a6b//+7+dnmdXnxFQAADwgODgYB06dMjTZRiLRbIAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAcJtRo0Zp6NChtX7c559/Xl26dKn145qI+6AAANyiskcWuJMrH4fwwgsvyAufBFOnEFAAALiN3W73dAn1HlM8AIB66w9/+IM6deqk4OBgNW3aVAkJCbpx40aFKZ5r164pKSlJDRs2VFRUlFatWqWHH35YkyZNcrRp2bKlFi1apNGjRyskJEQtWrTQxo0bnY43depUtW3bVg0aNFCrVq00a9Ys3bp1q5bO1rsQUAAA9dKXX36pX/3qVxo9erTOnj2rt956S4899lilUzupqak6evSo9u7dq4MHD+rtt9/We++9V6HdihUr1L17d2VnZ2vChAkaP368cnJyHO+HhIRo27ZtOnPmjF544QVt2rRJq1atcut5eiumeAAA9dKXX36p0tJSPfbYY7rnnnskSZ06darQ7tq1a9q+fbt27typ/v37S5K2bt2q6OjoCm0HDBigCRMmSPp2tGTVqlV688031a5dO0nSzJkzHW1btmypZ599VmlpaZoyZYrLz8/bEVAAAPXS/fffr/79+6tTp05KTEzUI488ol/84hdq3LixU7sLFy7o1q1b6tmzp2Ob3W53hI7v69y5s+PPNptNkZGRunLlimPb7373O61Zs0affvqprl+/rtLSUoWGhrrh7LwfUzwAgHrJ19dXBw8e1J/+9Cd17NhRa9euVbt27ZSbm1vjffr7+zv9bLPZVF5eLknKyMhQUlKSBgwYoNdff13Z2dmaMWOGSkpK7uo86ioCCgCg3rLZbOrVq5fmzp2r7OxsBQQEaPfu3U5tWrVqJX9/f508edKxraCgQB9//HG1jnXs2DHdc889mjFjhrp37642bdros88+c8l51EVM8QAA6qXMzEylp6frkUceUXh4uDIzM/XVV1+pQ4cO+uCDDxztQkJCNHLkSD333HNq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2kE/1w76ufaY0Nc2qyrX+gAAANQinsUDAACMQ0ABAADGIaAAAADjEFAAAIBx6mVAWbdunVq2bKmgoCDFxcXpxIkTP9h+165dat++vYKCgtSpUyft37+/lir1btXp502bNqlPnz5q3LixGjdurISEhDv+veBb1f19/k5aWppsNpuGDh3q3gLriOr289WrV5WcnKyoqCgFBgaqbdu2/LejCqrbz6tXr1a7du0UHBysmJgYTZ48WUVFRbVUrXc6cuSIBg8erOjoaNlsNu3Zs+eOn3nrrbf0wAMPKDAwUP/yL/+ibdu2ub1OWfVMWlqaFRAQYL388svWRx99ZI0dO9YKCwuz8vPzK21/9OhRy9fX11q2bJl15swZa+bMmZa/v791+vTpWq7cu1S3n5988klr3bp1VnZ2tnX27Flr1KhRlt1utz7//PNarty7VLefv5Obm2v96Ec/svr06WMNGTKkdor1YtXt5+LiYqt79+7WgAEDrHfeecfKzc213nrrLevUqVO1XLl3qW4/79ixwwoMDLR27Nhh5ebmWm+88YYVFRVlTZ48uZYr9y779++3ZsyYYf3xj3+0JFm7d+/+wfYXLlywGjRoYKWmplpnzpyx1q5da/n6+loHDhxwa531LqD07NnTSk5OdvxcVlZmRUdHW4sXL660/eOPP24NHDjQaVtcXJz1m9/8xq11ervq9vPtSktLrZCQEGv79u3uKrFOqEk/l5aWWg899JC1efNma+TIkQSUKqhuP69fv95q1aqVVVJSUlsl1gnV7efk5GSrX79+TttSU1OtXr16ubXOuqQqAWXKlCnWvffe67TtiSeesBITE91YmWXVqymekpISZWVlKSEhwbHNx8dHCQkJysjIqPQzGRkZTu0lKTEx8Z+2R836+XY3b97UrVu31KRJE3eV6fVq2s/z5s1TeHi4xowZUxtler2a9PPevXsVHx+v5ORkRURE6L777tOiRYtUVlZWW2V7nZr080MPPaSsrCzHNNCFCxe0f/9+DRgwoFZqri889T3oFU8zdpWvv/5aZWVlFe5WGxERoXPnzlX6mby8vErb5+Xlua1Ob1eTfr7d1KlTFR0dXeEfBf6hJv38zjvvaMuWLTp16lQtVFg31KSfL1y4oMOHDyspKUn79+/XJ598ogkTJujWrVuaM2dObZTtdWrSz08++aS+/vpr9e7dW5ZlqbS0VP/xH/+h3/72t7VRcr3xz74HCwsL9fe//13BwcFuOW69GkGBd1iyZInS0tK0e/duBQUFebqcOuPatWsaMWKENm3apGbNmnm6nDqtvLxc4eHh2rhxo7p166YnnnhCM2bM0IYNGzxdWp3y1ltvadGiRXrppZf03nvv6Y9//KP27dun+fPne7o0uEC9GkFp1qyZfH19lZ+f77Q9Pz9fkZGRlX4mMjKyWu1Rs37+zvLly7VkyRIdOnRInTt3dmeZXq+6/fzpp5/q4sWLGjx4sGNbeXm5JMnPz085OTlq3bq1e4v2QjX5fY6KipK/v798fX0d2zp06KC8vDyVlJQoICDArTV7o5r086xZszRixAj9+te/liR16tRJN27c0Lhx4zRjxgz5+PD/4K7wz74HQ0ND3TZ6ItWzEZSAgAB169ZN6enpjm3l5eVKT09XfHx8pZ+Jj493ai9JBw8e/KftUbN+lqRly5Zp/vz5OnDggLp3714bpXq16vZz+/btdfr0aZ06dcrx+vnPf66f/vSnOnXqlGJiYmqzfK9Rk9/nXr166ZNPPnEEQEn6+OOPFRUVRTj5J2rSzzdv3qwQQr4LhRaPmXMZj30PunUJroHS0tKswMBAa9u2bdaZM2escePGWWFhYVZeXp5lWZY1YsQIa9q0aY72R48etfz8/Kzly5dbZ8+etebMmcNlxlVQ3X5esmSJFRAQYP3hD3+wvvzyS8fr2rVrnjoFr1Ddfr4dV/FUTXX7+dKlS1ZISIiVkpJi5eTkWK+//roVHh5uLViwwFOn4BWq289z5syxQkJCrNdee826cOGC9ec//9lq3bq19fjjj3vqFLzCtWvXrOzsbCs7O9uSZK1cudLKzs62PvvsM8uyLGvatGnWiBEjHO2/u8z4ueees86ePWutW7eOy4zdZe3atVaLFi2sgIAAq2fPntbx48cd7/Xt29caOXKkU/vf//73Vtu2ba2AgADr3nvvtfbt21fLFXun6vTzPffcY0mq8JozZ07tF+5lqvv7/H0ElKqrbj8fO3bMiouLswIDA61WrVpZCxcutEpLS2u5au9TnX6+deuW9fzzz1utW7e2goKCrJiYGGvChAnW3/72t9ov3Iu8+eablf739ru+HTlypNW3b98Kn+nSpYsVEBBgtWrVytq6davb67RZFuNgAADALPVqDQoAAPAOBBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGOf/AbM+raqHP0BfAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "rng = np.random.default_rng(1)\n", "\n", "s = rng.normal(0.5, 0.05, size=1000)\n", "b = rng.exponential(1, size=1000)\n", "b = b[b < 1]\n", "\n", "ns, xe = np.histogram(s, bins=100, range=(0, 1))\n", "nb, _ = np.histogram(b, bins=xe)\n", "n = ns + nb\n", "\n", "plt.stairs(nb, xe, color=\"C1\", fill=True, label=\"background\")\n", "plt.stairs(n, xe, baseline=nb, color=\"C0\", fill=True, label=\"signal\")\n", "plt.legend();" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Fitting a parametric component and a template\n", "\n", "We now model the peaking component parametrically with a Gaussian. A template fit is an extended binned fit, so we need to provide a scaled cumulative density function like for `iminuit.cost.ExtendedBinnedNLL`. To obtain a background template, we generate more samples from the exponential distribution and make a histogram." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 87.01 (chi2/ndof = 0.9) Nfcn = 194
EDM = 4.93e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0_n 990 40 0
1 x0_mu 0.4951 0.0020 0 1
2 x0_sigma 0.0484 0.0018 0
3 x1 630 40 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0_n x0_mu x0_sigma x1
x0_n 1.43e+03 0.000324 (0.004) 0.0185 (0.274) -441 (-0.284)
x0_mu 0.000324 (0.004) 3.87e-06 -5.26e-08 (-0.015) -0.000347 (-0.004)
x0_sigma 0.0185 (0.274) -5.26e-08 (-0.015) 3.21e-06 -0.0185 (-0.251)
x1 -441 (-0.284) -0.000347 (-0.004) -0.0185 (-0.251) 1.69e+03
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 87.01 (chi2/ndof = 0.9) │ Nfcn = 194 │\n", "│ EDM = 4.93e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0_n │ 990 │ 40 │ │ │ 0 │ │ │\n", "│ 1 │ x0_mu │ 0.4951 │ 0.0020 │ │ │ 0 │ 1 │ │\n", "│ 2 │ x0_sigma │ 0.0484 │ 0.0018 │ │ │ 0 │ │ │\n", "│ 3 │ x1 │ 630 │ 40 │ │ │ 0 │ │ │\n", "└───┴──────────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌──────────┬─────────────────────────────────────────┐\n", "│ │ x0_n x0_mu x0_sigma x1 │\n", "├──────────┼─────────────────────────────────────────┤\n", "│ x0_n │ 1.43e+03 0.000324 0.0185 -441 │\n", "│ x0_mu │ 0.000324 3.87e-06 -5.26e-08 -0.000347 │\n", "│ x0_sigma │ 0.0185 -5.26e-08 3.21e-06 -0.0185 │\n", "│ x1 │ -441 -0.000347 -0.0185 1.69e+03 │\n", "└──────────┴─────────────────────────────────────────┘" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# signal model: scaled cdf of a normal distribution\n", "def signal(xe, n, mu, sigma):\n", " return n * norm.cdf(xe, mu, sigma)\n", "\n", "# background template: histogram of MC simulation\n", "rng = np.random.default_rng(2)\n", "b2 = rng.exponential(1, size=1000)\n", "b2 = b2[b2 < 1]\n", "template = np.histogram(b2, bins=xe)[0]\n", "\n", "# fit\n", "c = Template(n, xe, (signal, template))\n", "m = Minuit(c, x0_n=500, x0_mu=0.5, x0_sigma=0.05, x1=100)\n", "m.limits[\"x0_n\", \"x1\", \"x0_sigma\"] = (0, None)\n", "m.limits[\"x0_mu\"] = (0, 1)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/png": "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pU+fPjAYDKirq3PYX1dXh/T0dMXHpKen+3Q8+XedJStWrMDSpUuxdetWXHbZZYEMM+z5ep3/+9//4v/+7/9w8803y/va29sBAD169MChQ4dw0UUXBTboMOTP+zkjIwM9e/aEwWCQ91166aWora3FuXPnEB0dHdCYw5E/1/nxxx/HtGnT8Ktf/QoAMGLECFitVsycOROPPfYYoqL4P7gW3P0dTExMDFjrCRBhLSjR0dG46qqrsG3bNnlfe3s7tm3bhpycHMXH5OTkOBwPAB999JHb48m/6wwAy5cvx29/+1ts2bIFV199dTBCDWu+XudLLrkEX331Fb788kv5dsstt+Daa6/Fl19+iezs7GCGHzb8eT+PHj0aR44ckRNAAPj222+RkZHB5MQNf65zc3OzSxIiJYUil5nTTMj+DgZ0CK4Ovf3222JMTIz42muviQcPHhRnzpwpJicni7W1taIoiuK0adPEBQsWyMf/4x//EHv06CGuWLFC/Oabb8Qnn3ySZcYq+Hqdly5dKkZHR4ulpaViTU2NfDtz5kyoXkJY8PU6O2MVjzq+Xufvv/9eTEhIEO+77z7x0KFD4ubNm8V+/fqJTz/9dKheQljw9To/+eSTYkJCgvjWW2+JR48eFf/+97+LF110kXjbbbeF6iWEhTNnzoj79+8X9+/fLwIQV65cKe7fv1/87rvvRFEUxQULFojTpk2Tj5fKjB966CHxm2++EV944QWWGQfK6tWrxf79+4vR0dHiyJEjxc8++0y+75prrhGnT5/ucPw777wjDh48WIyOjhaHDRsm/uUvfwlyxOHJl+t8wQUXiABcbk8++WTwAw8zvr6f7TFBUc/X67xr1y5x1KhRYkxMjHjhhReKv/vd78Tz588HOerw48t1bmtrExctWiRedNFFYmxsrJidnS3ee++94unTp4MfeBj55JNPFL9vpWs7ffp08ZprrnF5zOWXXy5GR0eLF154ofjqq68GPE5BFNkORkRERPoSUWNQiIiIKDwwQSEiIiLdYYJCREREusMEhYiIiHSHCQoRERHpDhMUIiIi0h0mKERERKQ7TFCIiIhId5igEBERke4wQSEiIiLdYYJCREREusMEhYiIiHTn/wN1ymumgQVVkgAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m.visualize()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The fit succeeded and the statistical uncertainty in the template is propagated into the result. You can play with this demo and see what happens if you increase the statistic of the template.\n", "\n", "Note: the parameters of a parametric components are prefixed with `xn_` where `n` is the index of the component. This is to avoid name clashes between the parameter names of individual components and for clarity which parameter belongs to which component." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Extreme case: Fitting two parametric components\n", "\n", "Although this is not recommended, you can also use the `Template` class with two parametric components and no template. If you are in that situation, however, it is simpler and more efficient to use `iminuit.cost.ExtendedBinnedNLL`. The following snipped is therefore just a proof that `iminuit.cost.Template` handles this limiting case as well." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 92.87 (chi2/ndof = 1.0) Nfcn = 190
EDM = 1.67e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0_n 990 40 0
1 x0_mu 0.4964 0.0019 0 1
2 x0_sigma 0.0487 0.0016 0
3 x1_n 629 29 0
4 x1_mu 0.97 0.14 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0_n x0_mu x0_sigma x1_n x1_mu
x0_n 1.3e+03 0.000424 (0.006) 0.0123 (0.209) -264 (-0.252) -0.369 (-0.076)
x0_mu 0.000424 (0.006) 3.44e-06 -9.8e-08 (-0.032) 0.000293 (0.005) -1.64e-05 (-0.065)
x0_sigma 0.0123 (0.209) -9.8e-08 (-0.032) 2.64e-06 -0.0129 (-0.271) -1.64e-05 (-0.075)
x1_n -264 (-0.252) 0.000293 (0.005) -0.0129 (-0.271) 851 0.337 (0.085)
x1_mu -0.369 (-0.076) -1.64e-05 (-0.065) -1.64e-05 (-0.075) 0.337 (0.085) 0.0183
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 92.87 (chi2/ndof = 1.0) │ Nfcn = 190 │\n", "│ EDM = 1.67e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0_n │ 990 │ 40 │ │ │ 0 │ │ │\n", "│ 1 │ x0_mu │ 0.4964 │ 0.0019 │ │ │ 0 │ 1 │ │\n", "│ 2 │ x0_sigma │ 0.0487 │ 0.0016 │ │ │ 0 │ │ │\n", "│ 3 │ x1_n │ 629 │ 29 │ │ │ 0 │ │ │\n", "│ 4 │ x1_mu │ 0.97 │ 0.14 │ │ │ 0 │ │ │\n", "└───┴──────────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌──────────┬───────────────────────────────────────────────────┐\n", "│ │ x0_n x0_mu x0_sigma x1_n x1_mu │\n", "├──────────┼───────────────────────────────────────────────────┤\n", "│ x0_n │ 1.3e+03 0.000424 0.0123 -264 -0.369 │\n", "│ x0_mu │ 0.000424 3.44e-06 -9.8e-08 0.000293 -1.64e-05 │\n", "│ x0_sigma │ 0.0123 -9.8e-08 2.64e-06 -0.0129 -1.64e-05 │\n", "│ x1_n │ -264 0.000293 -0.0129 851 0.337 │\n", "│ x1_mu │ -0.369 -1.64e-05 -1.64e-05 0.337 0.0183 │\n", "└──────────┴───────────────────────────────────────────────────┘" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# signal model: scaled cdf of a normal distribution\n", "def signal(xe, n, mu, sigma):\n", " return n * norm.cdf(xe, mu, sigma)\n", "\n", "# background model: scaled cdf a an exponential distribution\n", "def background(xe, n, mu):\n", " return n * truncexpon.cdf(xe, xe[0], xe[-1], 0, mu)\n", "\n", "# fit\n", "c = Template(n, xe, (signal, background))\n", "m = Minuit(c, x0_n=500, x0_mu=0.5, x0_sigma=0.05, x1_n=100, x1_mu=1)\n", "m.limits[\"x0_n\", \"x1_n\", \"x0_sigma\", \"x1_mu\"] = (0, None)\n", "m.limits[\"x0_mu\"] = (0, 1)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "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m0QrYV08t912mx577DE9/fTT2r17t26++Wa1trbq2muvTVSVAACQ1P26S5JRQrp4JOmKK67QZ599pnnz5qmhoUFnn322XnrppU4nzgIAkAjdqbskGSVkmHG0whmmBAAAnCGc3++kGMUDAAB6FgIKAABwHAIKAABwHAIKAABwHAIKAABwHAIKAABwHAIKAABwHAIKAABwHAIKAABwnIRd6j4anovfNjc3J7gmAADAKs/vtpWL2CdlQDl8+LAkqbCwMME1AQAA4Tp8+LCysrK6LJOUc/G0t7dr37596tu3b6epsKPV3NyswsJC1dXVMc9PDHGc44PjHB8c5/jgOMdPrI61aZo6fPiwCgoKlJLS9VkmSdmCkpKSIrfbHdPHyMzM5AMQBxzn+OA4xwfHOT44zvETi2MdquXEg5NkAQCA4xBQAACA4xBQOkhNTdU999yj1NTURFelW+M4xwfHOT44zvHBcY4fJxzrpDxJFgAAdG+0oAAAAMchoAAAAMchoAAAAMchoAAAAMfpkQFl+fLlGjJkiNLS0jRmzBht3769y/Jr167V8OHDlZaWprPOOktVVVVxqmlyC+c4P/bYYyouLtYpp5yiU045RSUlJSFfF5wQ7vvZY82aNTIMQ5MmTYptBbuJcI/zoUOHNH36dOXn5ys1NVVnnHEG3x0WhHuclyxZom9961vq06ePCgsLNWvWLB05ciROtU1OW7Zs0aWXXqqCggIZhqHnn38+5H02b96sc889V6mpqfqnf/onPfXUUzGvp8weZs2aNWbv3r3NJ554wty5c6d5/fXXm/369TMbGxsDln/ttddMl8tlLlq0yNy1a5d51113mSeddJL57rvvxrnmySXc43z11Veby5cvN99++21z9+7d5o9//GMzKyvL3Lt3b5xrnlzCPc4etbW15qBBg8zi4mKztLQ0PpVNYuEe56NHj5qjRo0yJ06caP7lL38xa2trzc2bN5vvvPNOnGueXMI9zqtWrTJTU1PNVatWmbW1tebLL79s5ufnm7NmzYpzzZNLVVWVeeedd5qVlZWmJHP9+vVdlv/oo4/Mk08+2bztttvMXbt2mQ899JDpcrnMl156Kab17HEBZfTo0eb06dO9621tbWZBQYG5YMGCgOUvv/xy80c/+pHftjFjxpg33nhjTOuZ7MI9zh0dP37c7Nu3r/n000/HqordQiTH+fjx4+YFF1xg/va3vzWnTZtGQLEg3OP8yCOPmKeddpp57NixeFWxWwj3OE+fPt38/ve/77fttttuM8eOHRvTenYnVgLK7NmzzZEjR/ptu+KKK8wJEybEsGam2aO6eI4dO6YdO3aopKTEuy0lJUUlJSXaunVrwPts3brVr7wkTZgwIWh5RHacO/riiy/01VdfKTs7O1bVTHqRHuf58+crJydH1113XTyqmfQiOc6///3vVVRUpOnTpys3N1ff/va3dd9996mtrS1e1U46kRznCy64QDt27PB2A3300UeqqqrSxIkT41LnniJRv4NJOVlgpD7//HO1tbUpNzfXb3tubq7ee++9gPdpaGgIWL6hoSFm9Ux2kRznju644w4VFBR0+lDgG5Ec57/85S96/PHH9c4778Shht1DJMf5o48+0iuvvKIpU6aoqqpKH3zwgW655RZ99dVXuueee+JR7aQTyXG++uqr9fnnn+vCCy+UaZo6fvy4brrpJv385z+PR5V7jGC/g83Nzfryyy/Vp0+fmDxuj2pBQXJYuHCh1qxZo/Xr1ystLS3R1ek2Dh8+rGuuuUaPPfaYBgwYkOjqdGvt7e3KycnRo48+qvPOO09XXHGF7rzzTq1YsSLRVetWNm/erPvuu08PP/yw3nrrLVVWVuoPf/iDfvWrXyW6arBBj2pBGTBggFwulxobG/22NzY2Ki8vL+B98vLywiqPyI6zxwMPPKCFCxfqz3/+s77zne/EsppJL9zj/OGHH+rjjz/WpZde6t3W3t4uSerVq5f27NmjYcOGxbbSSSiS93N+fr5OOukkuVwu77YzzzxTDQ0NOnbsmHr37h3TOiejSI7z3XffrWuuuUY/+clPJElnnXWWWltbdcMNN+jOO+9USgr/g9sh2O9gZmZmzFpPpB7WgtK7d2+dd9552rRpk3dbe3u7Nm3apKKiooD3KSoq8isvSX/605+Clkdkx1mSFi1apF/96ld66aWXNGrUqHhUNamFe5yHDx+ud999V++88453+dd//VeNHz9e77zzjgoLC+NZ/aQRyft57Nix+uCDD7wBUJLef/995efnE06CiOQ4f/HFF51CiCcUmkwzZ5uE/Q7G9BRcB1qzZo2ZmppqPvXUU+auXbvMG264wezXr5/Z0NBgmqZpXnPNNeacOXO85V977TWzV69e5gMPPGDu3r3bvOeeexhmbEG4x3nhwoVm7969zYqKCnP//v3e5fDhw4l6Ckkh3OPcEaN4rAn3OH/66adm3759zVtvvdXcs2ePuXHjRjMnJ8e89957E/UUkkK4x/mee+4x+/btaz777LPmRx99ZP7xj380hw0bZl5++eWJegpJ4fDhw+bbb79tvv3226Yk88EHHzTffvtt85NPPjFN0zTnzJljXnPNNd7ynmHGt99+u7l7925z+fLlDDOOlYceesg89dRTzd69e5ujR482//rXv3pv+973vmdOmzbNr/xzzz1nnnHGGWbv3r3NkSNHmn/4wx/iXOPkFM5xHjx4sCmp03LPPffEv+JJJtz3sy8CinXhHufXX3/dHDNmjJmammqedtpp5q9//Wvz+PHjca518gnnOH/11VfmL37xC3PYsGFmWlqaWVhYaN5yyy3mP/7xj/hXPIlUV1cH/L71HNtp06aZ3/ve9zrd5+yzzzZ79+5tnnbaaeaTTz4Z83oapkk7GAAAcJYedQ4KAABIDgQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOAQUAADgOP8fyoeT2U0GM4sAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m.visualize()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The result is identical when we fit with `iminuit.cost.ExtendedBinnedNLL`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Migrad
FCN = 92.87 (chi2/ndof = 1.0) Nfcn = 190
EDM = 1.67e-05 (Goal: 0.0002)
Valid Minimum No Parameters at limit
Below EDM threshold (goal x 10) Below call limit
Covariance Hesse ok Accurate Pos. def. Not forced
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x0_n 990 40 0
1 x0_mu 0.4964 0.0019 0 1
2 x0_sigma 0.0487 0.0016 0
3 x1_n 629 29 0
4 x1_mu 0.97 0.14 0
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0_n x0_mu x0_sigma x1_n x1_mu
x0_n 1.3e+03 0.000424 (0.006) 0.0123 (0.209) -264 (-0.252) -0.369 (-0.076)
x0_mu 0.000424 (0.006) 3.44e-06 -9.8e-08 (-0.032) 0.000293 (0.005) -1.64e-05 (-0.065)
x0_sigma 0.0123 (0.209) -9.8e-08 (-0.032) 2.64e-06 -0.0129 (-0.271) -1.64e-05 (-0.075)
x1_n -264 (-0.252) 0.000293 (0.005) -0.0129 (-0.271) 851 0.337 (0.085)
x1_mu -0.369 (-0.076) -1.64e-05 (-0.065) -1.64e-05 (-0.075) 0.337 (0.085) 0.0183
" ], "text/plain": [ "┌─────────────────────────────────────────────────────────────────────────┐\n", "│ Migrad │\n", "├──────────────────────────────────┬──────────────────────────────────────┤\n", "│ FCN = 92.87 (chi2/ndof = 1.0) │ Nfcn = 190 │\n", "│ EDM = 1.67e-05 (Goal: 0.0002) │ │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Valid Minimum │ No Parameters at limit │\n", "├──────────────────────────────────┼──────────────────────────────────────┤\n", "│ Below EDM threshold (goal x 10) │ Below call limit │\n", "├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤\n", "│ Covariance │ Hesse ok │ Accurate │ Pos. def. │ Not forced │\n", "└───────────────┴──────────────────┴───────────┴─────────────┴────────────┘\n", "┌───┬──────────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐\n", "│ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │\n", "├───┼──────────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤\n", "│ 0 │ x0_n │ 990 │ 40 │ │ │ 0 │ │ │\n", "│ 1 │ x0_mu │ 0.4964 │ 0.0019 │ │ │ 0 │ 1 │ │\n", "│ 2 │ x0_sigma │ 0.0487 │ 0.0016 │ │ │ 0 │ │ │\n", "│ 3 │ x1_n │ 629 │ 29 │ │ │ 0 │ │ │\n", "│ 4 │ x1_mu │ 0.97 │ 0.14 │ │ │ 0 │ │ │\n", "└───┴──────────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘\n", "┌──────────┬───────────────────────────────────────────────────┐\n", "│ │ x0_n x0_mu x0_sigma x1_n x1_mu │\n", "├──────────┼───────────────────────────────────────────────────┤\n", "│ x0_n │ 1.3e+03 0.000424 0.0123 -264 -0.369 │\n", "│ x0_mu │ 0.000424 3.44e-06 -9.8e-08 0.000293 -1.64e-05 │\n", "│ x0_sigma │ 0.0123 -9.8e-08 2.64e-06 -0.0129 -1.64e-05 │\n", "│ x1_n │ -264 0.000293 -0.0129 851 0.337 │\n", "│ x1_mu │ -0.369 -1.64e-05 -1.64e-05 0.337 0.0183 │\n", "└──────────┴───────────────────────────────────────────────────┘" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def total(xe, x0_n, x0_mu, x0_sigma, x1_n, x1_mu):\n", " return signal(xe, x0_n, x0_mu, x0_sigma) + background(xe, x1_n, x1_mu)\n", "\n", "c = ExtendedBinnedNLL(n, xe, total)\n", "m = Minuit(c, x0_n=500, x0_mu=0.5, x0_sigma=0.05, x1_n=100, x1_mu=1)\n", "m.limits[\"x0_n\", \"x1_n\", \"x0_sigma\", \"x1_mu\"] = (0, None)\n", "m.limits[\"x0_mu\"] = (0, 1)\n", "m.migrad()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/png": "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/PPP1/bt27sMnI23jIwM3X333V26lGAt6jk+qOf4oJ7jg3qOHzvUdVLuxQMAAHo29uIBAAC2Q4ICAABshwQFAADYDgkKAACwnZRMUNauXauysjJlZmZqzJgxeuONN0KWf+6553TWWWcpMzNT55xzjrZt2xanSJNbOPX8+OOPa8KECerXr5/69eun8vLybn8vOC3cz7PXpk2b5HA4NGPGjNgG2EOEW8/Hjh3T/PnzVVRUpIyMDH31q1/l3w4Twq3n1atXa8SIEerdu7dKS0u1cOFCtba2xina5LRz505dccUVKi4ulsPh0ObNm7t9zSuvvKKvfe1rysjI0D/8wz/oySefjHmcMlLMpk2bjPT0dOOnP/2p8e677xo33HCD0bdvX6OxsTFg+ddee81wOp3GqlWrjPfee8+48847jTPOOMN4++234xx5cgm3nq+55hpj7dq1xptvvmm8//77xnXXXWfk5eUZdXV1cY48uYRbz1779+83Bg8ebEyYMMGYPn16fIJNYuHWc1tbm3HRRRcZU6dONX7/+98b+/fvN1555RXjrbfeinPkySXcen7mmWeMjIwM45lnnjH2799v/PrXvzaKioqMhQsXxjny5LJt2zbjjjvuMFwulyHJeP7550OW//DDD40+ffoYixYtMt577z3j4YcfNpxOp7F9+/aYxplyCcrFF19szJ8/33fs8XiM4uJiY+XKlQHLX3nllcbll1/ud27MmDHGjTfeGNM4k1249dzZ559/buTk5BhPPfVUrELsESKp588//9wYN26c8d///d/GnDlzSFBMCLeeH3vsMeMrX/mKcfLkyXiF2COEW8/z5883vvWtb/mdW7RokTF+/PiYxtmTmElQbrvtNmP06NF+56666ipjypQpMYzMMFKqi+fkyZPau3evysvLfefS0tJUXl6u3bt3B3zN7t27/cpL0pQpU4KWR2T13NmJEyd06tQp9e/fP1ZhJr1I6/mee+7RoEGDNHfu3HiEmfQiqedf/epXGjt2rObPn6+CggKdffbZWrFihTweT7zCTjqR1PO4ceO0d+9eXzfQhx9+qG3btmnq1KlxiTlVJOrvYFLsZmyVQ4cOyePxdFmttqCgQH/+858DvqahoSFg+YaGhpjFmewiqefObr/9dhUXF3f5UuBLkdTz73//e23YsEFvvfVWHCLsGSKp5w8//FAvv/yyZs2apW3btumvf/2rbrrpJp06dUp33313PMJOOpHU8zXXXKNDhw7p0ksvlWEY+vzzz/W9731PS5cujUfIKSPY38Hm5mZ99tln6t27d0zeN6VaUJAc7rvvPm3atEnPP/+8MjMzEx1Oj3H8+HFde+21evzxx5Wfn5/ocHq09vZ2DRo0SOvXr9eFF16oq666SnfccYfWrVuX6NB6lFdeeUUrVqzQo48+qj/+8Y9yuVx68cUX9YMf/CDRocECKdWCkp+fL6fTqcbGRr/zjY2NKiwsDPiawsLCsMojsnr2euCBB3Tffffpt7/9rc4999xYhpn0wq3nv/3tb/r73/+uK664wneuvb1dktSrVy/t27dPZ555ZmyDTkKRfJ6Liop0xhlnyOl0+s6NHDlSDQ0NOnnypNLT02MaczKKpJ6XLVuma6+9Vv/6r/8qSTrnnHPkdrs1b9483XHHHUpL4//gVgj2dzA3NzdmrSdSirWgpKen68ILL9SOHTt859rb27Vjxw6NHTs24GvGjh3rV16SXnrppaDlEVk9S9KqVav0gx/8QNu3b9dFF10Uj1CTWrj1fNZZZ+ntt9/WW2+95Xv80z/9ky677DK99dZbKi0tjWf4SSOSz/P48eP117/+1ZcAStJf/vIXFRUVkZwEEUk9nzhxoksS4k0KDbaZs0zC/g7GdAiuDW3atMnIyMgwnnzySeO9994z5s2bZ/Tt29doaGgwDMMwrr32WmPx4sW+8q+99prRq1cv44EHHjDef/994+6772aasQnh1vN9991npKenG9XV1caBAwd8j+PHjyfqFpJCuPXcGbN4zAm3nj/++GMjJyfHuPnmm419+/YZW7duNQYNGmTce++9ibqFpBBuPd99991GTk6O8fOf/9z48MMPjd/85jfGmWeeaVx55ZWJuoWkcPz4cePNN9803nzzTUOS8eCDDxpvvvmm8dFHHxmGYRiLFy82rr32Wl957zTjW2+91Xj//feNtWvXMs04Vh5++GFjyJAhRnp6unHxxRcbr7/+uu+5b3zjG8acOXP8yj/77LPGV7/6VSM9Pd0YPXq08eKLL8Y54uQUTj0PHTrUkNTlcffdd8c/8CQT7ue5IxIU88Kt5127dhljxowxMjIyjK985SvGD3/4Q+Pzzz+Pc9TJJ5x6PnXqlLF8+XLjzDPPNDIzM43S0lLjpptuMo4ePRr/wJPI7373u4D/3nrrds6cOcY3vvGNLq85//zzjfT0dOMrX/mK8cQTT8Q8Todh0A4GAADsJaXGoAAAgORAggIAAGyHBAUAANgOCQoAALAdEhQAAGA7JCgAAMB2SFAAAIDtkKAAAADbIUEBAAC2Q4ICAABshwQFAADYDgkKAACwnf8PnGX4Ai7lLKIAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m.visualize()" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "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.9.16" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "bdbf20ff2e92a3ae3002db8b02bd1dd1b287e934c884beb29a73dced9dbd0fa3" } } }, "nbformat": 4, "nbformat_minor": 2 } iminuit-2.24.0/doc/plots/bench.py0000644000000000000000000001334414332717401013551 0ustar00import numpy as np from numpy.random import default_rng from matplotlib import pyplot as plt import matplotlib as mpl from matplotlib.ticker import LogLocator import os import pickle mpl.rcParams.update(mpl.rcParamsDefault) class TrackingFcn: errordef = 1 def __init__(self, rng, npar): self.ncall = 0 self.y = 5 * rng.standard_normal(npar) def __call__(self, par, *args): self.ncall += 1 # make problem non-linear z = self.y - par return np.sum(z**2 + 0.1 * z**4) class Runner: def __init__(self, npars): self.npars = npars def __call__(self, seed): from iminuit import Minuit import nlopt from scipy.optimize import minimize data = [] rng = default_rng(seed) for npar in self.npars: fcn = TrackingFcn(rng, npar) for stra in (0, 1, 2): key = f"Minuit2/strategy={stra}" print(key, npar) fcn.ncall = 0 m = Minuit(fcn, np.zeros(npar)) m.strategy = stra m.migrad() max_dev = np.max(np.abs(m.np_values() - fcn.y)) data.append((key, npar, fcn.ncall, max_dev)) for algo in ("BOBYQA", "NEWUOA", "PRAXIS", "SBPLX"): if npar == 1 and algo == "PRAXIS": continue # PRAXIS does not work for npar==1 print(algo, npar) fcn.ncall = 0 opt = nlopt.opt(getattr(nlopt, "LN_" + algo), npar) opt.set_min_objective(lambda par, grad: fcn(par)) opt.set_xtol_abs(1e-2) try: xopt = opt.optimize(np.zeros(npar)) max_dev = np.max(np.abs(xopt - fcn.y)) key = f"nlopt/{algo}" data.append((key, npar, fcn.ncall, max_dev)) except Exception: pass for algo in ("BFGS", "CG", "Powell", "Nelder-Mead"): print(algo, npar) fcn.ncall = 0 result = minimize(fcn, np.zeros(npar), method=algo, jac=False) max_dev = np.max(np.abs(result.x - fcn.y)) key = f"scipy/{algo}" data.append((key, npar, fcn.ncall, max_dev)) return data if os.path.exists("bench.pkl"): with open("bench.pkl", "rb") as f: results = pickle.load(f) else: npars = (1, 2, 3, 4, 6, 10, 20, 30, 40, 60, 100) from numpy.random import SeedSequence from concurrent.futures import ProcessPoolExecutor as Pool sg = SeedSequence(1) with Pool() as p: results = tuple(p.map(Runner(npars), sg.spawn(16))) with open("bench.pkl", "wb") as f: pickle.dump(results, f) # plt.figure() # f = TrackingFcn(default_rng(), 2) # x = np.linspace(-10, 10) # X, Y = np.meshgrid(x, x) # F = np.empty_like(X) # for i, xi in enumerate(x): # for j, yi in enumerate(x): # F[i, j] = f((xi, yi)) # plt.pcolormesh(X, Y, F.T) # plt.colorbar() methods = {} for data in results: for key, npar, ncal, maxdev in data: methods.setdefault(key, {}).setdefault(npar, []).append((ncal, maxdev)) fig, ax = plt.subplots(1, 2, figsize=(10, 5), sharex=True) plt.subplots_adjust( top=0.96, bottom=0.14, left=0.075, right=0.81, hspace=0.2, wspace=0.25 ) handles = [] labels = [] markers = ( ("o", 10), ("s", 7), ("D", 7), ("<", 7), (">", 7), ("^", 7), ("v", 7), ("*", 9), ("X", 7), ("P", 7), ("p", 8), ) for method, (m, ms) in zip(sorted(methods), markers): ls = "-" lw = 1 zorder = None color = None mfc = None mew = 1 if "Minuit" in method: ls = "-" lw = 2 zorder = 10 color = "k" mfc = "w" mew = 2 data = methods[method] npars = np.sort(list(data)) ncalls = np.empty_like(npars) max_devs = np.empty_like(npars, dtype=float) for i, npar in enumerate(npars): nc, md = np.transpose(data[npar]) ncalls[i] = np.median(nc) max_devs[i] = np.median(md) plt.sca(ax[0]) (p,) = plt.plot( npars, ncalls / npars, ls=ls, lw=lw, marker=m, ms=ms, zorder=zorder, color=color, mfc=mfc, mew=mew, ) handles.append(p) labels.append(method) plt.xlabel("$N_\\mathrm{par}$") plt.ylabel("$N_\\mathrm{call}$ / $N_\\mathrm{par}$") plt.loglog() plt.ylim(8, 5e2) plt.xlim(0.7, 150) plt.sca(ax[1]) plt.xlabel("$N_\\mathrm{par}$") plt.ylabel("maximum deviation") plt.plot( npars, max_devs, lw=lw, ls=ls, marker=m, ms=ms, zorder=zorder, color=color, mfc=mfc, mew=mew, ) plt.loglog() plt.gca().yaxis.set_major_locator(LogLocator(numticks=100)) plt.figlegend(handles, labels, loc="center right", fontsize="small") plt.savefig("bench.svg") plt.figure(constrained_layout=True) plt.loglog() for method, (m, ms) in zip(sorted(methods), markers): zorder = None color = None mfc = None mew = 1 if "Minuit" in method: zorder = 10 color = "k" mfc = "w" mew = 2 data = methods[method] x = [] y = [] s = [] for npar in (2, 10, 100): if npar not in data: continue nc, md = np.transpose(data[npar]) x.append(np.median(nc) / npar) y.append(np.median(md)) s.append(50 * npar**0.5) plt.scatter(x, y, s, marker=m, color=mfc, edgecolor=color, zorder=zorder) plt.xlabel("$N_\\mathrm{call}$ / $N_\\mathrm{par}$") plt.ylabel("maximum deviation") plt.title("small: npar = 2, medium: npar = 10, large: npar = 100") plt.savefig("bench2d.svg") plt.show() iminuit-2.24.0/doc/plots/interactive.py0000644000000000000000000000101214332717401014774 0ustar00from iminuit import Minuit, cost import numpy as np from matplotlib import pyplot as plt # custom visualization, x and y are taken from outer scope def viz(args): plt.plot(x, y, "ok") xm = np.linspace(x[0], x[-1], 100) plt.plot(xm, model(xm, *args)) def model(x, a, b): return a + b * x x = np.array([1, 2, 3, 4, 5]) y = np.array([1.03, 1.58, 2.03, 2.37, 3.09]) c = cost.LeastSquares(x, y, 0.1, model) m = Minuit(c, 0.5, 0.5) m.interactive(viz) # calling m.interactive() uses LeastSquares.visualize iminuit-2.24.0/doc/plots/loss.py0000644000000000000000000000051714332717401013450 0ustar00from matplotlib import pyplot as plt import numpy as np def soft_l1(z): return 2 * ((1 + z) ** 0.5 - 1) x = np.linspace(-3, 3) z = x**2 plt.plot(x, z, label="linear $\\rho(z) = z$") plt.plot(x, soft_l1(z), label="soft L1-norm $\\rho(z) = 2(\\sqrt{1+z} - 1)$") plt.xlabel("studentized residual") plt.ylabel("cost") plt.legend() iminuit-2.24.0/doc/plots/mncontour.py0000644000000000000000000000032414332717401014510 0ustar00from iminuit import Minuit def cost(x, y, z): return (x - 1) ** 2 + (y - x) ** 2 + (z - 2) ** 2 cost.errordef = Minuit.LEAST_SQUARES m = Minuit(cost, x=0, y=0, z=0) m.migrad() m.draw_mncontour("x", "y") iminuit-2.24.0/doc/plots/mnmatrix.py0000644000000000000000000000042014332717401014320 0ustar00from iminuit import Minuit def cost(a, b, c): return ( a**2 + 0.25 * a**4 + a * b + ((b - 1) / 0.6) ** 2 - 2.5 * b * c + ((c - 2) / 0.5) ** 2 ) m = Minuit(cost, 1, 1, 1) m.migrad() m.draw_mnmatrix(cl=[1, 2, 3]) iminuit-2.24.0/doc/plots/mnprofile.py0000644000000000000000000000031714332717401014461 0ustar00from iminuit import Minuit def cost(x, y, z): return (x - 1) ** 2 + (y - x) ** 2 + (z - 2) ** 2 cost.errordef = Minuit.LEAST_SQUARES m = Minuit(cost, x=0, y=0, z=0) m.migrad() m.draw_mnprofile("y") iminuit-2.24.0/doc/reference.rst0000644000000000000000000000170614332717401013446 0ustar00.. include:: bibliography.txt .. _api: Reference ========= .. currentmodule:: iminuit Quick Summary ------------- These methods and properties you will probably use a lot: .. autosummary:: Minuit Minuit.migrad Minuit.hesse Minuit.minos Minuit.values Minuit.errors Minuit.merrors Minuit.fixed Minuit.limits Minuit.covariance Minuit.valid Minuit.accurate Minuit.fval Minuit.nfit Minuit.mnprofile Minuit.mncontour Minuit.visualize Minuit.draw_mnmatrix Main interface -------------- .. autoclass:: Minuit :members: :undoc-members: Scipy-like interface -------------------- .. autofunction:: minimize Cost functions -------------- .. automodule:: iminuit.cost :members: :inherited-members: Utilities --------- .. automodule:: iminuit.util :exclude-members: Matrix :inherited-members: .. autoclass:: iminuit.util.Matrix :members: :no-inherited-members: iminuit-2.24.0/doc/release.md0000644000000000000000000000150614332717401012716 0ustar00How to make a release ===================== - Sync local `main` and `develop` with Github - `for x in main develop; git checkout $x; git pull` - On `develop` branch - Update version in `pyproject.toml` - For a beta release, add `.betaN`, where N is a number >= 0 - For a release candidate, add `.rcN` - Run `python3 doc/update_changelog.py` or update `doc/changelog.rst` manually - Check the output if you used the script - Merge `develop` into `main` - Every push to `main` triggers building wheels, uploading to PyPI, and tagging/publishing on GitHub - If there are problems with the wheels, commit fixes to `develop`, then merge again into `main` - Note: Upload to PyPI uses API tokens configured in PyPI and Github "Secrets" - conda-forge should pick up our release automatically and generate conda packages iminuit-2.24.0/doc/root_version.py0000644000000000000000000000077314332717401014063 0ustar00import subprocess as subp from pathlib import PurePath project_dir = PurePath(__file__).parent.parent # check that root version is up-to-date git_submodule = subp.check_output( ["git", "submodule", "status"], cwd=project_dir ).decode() for item in git_submodule.strip().split("\n"): parts = item.split() if PurePath(parts[1]) != PurePath("extern") / "root": continue assert len(parts) == 3, "module is not checked out" root_version = parts[2][1:-1] # strip braces break iminuit-2.24.0/doc/studies.rst0000644000000000000000000000072714332717401013172 0ustar00.. include:: bibliography.txt .. _studies: Studies ======= The following studies explore different aspects of the library, including its performance. This content is intended for an advanced audience. .. toctree:: :maxdepth: 1 notebooks/binned_vs_unbinned notebooks/hesse_and_minos notebooks/numba notebooks/automatic_differentiation notebooks/cost_function_benchmarks notebooks/gof notebooks/template_gof notebooks/memory_layout iminuit-2.24.0/doc/tutorials.rst0000644000000000000000000000243614332717401013537 0ustar00.. include:: bibliography.txt .. _tutorials: Tutorials ========= The following tutorials show how to use iminuit and explore different aspects of the library. The order is the recommended reading order, the later entries are about more and more specialized applications. Important for most users are only the first two entries. .. toctree:: :maxdepth: 1 notebooks/basic notebooks/cost_functions notebooks/error_bands notebooks/interactive notebooks/simultaneous_fits notebooks/template_fits notebooks/template_model_mix notebooks/conditional_variable notebooks/scipy_and_constraints notebooks/roofit notebooks/external_minimizer notebooks/generic_least_squares notebooks/cython RooFit tutorials ---------------- The following tutorials correspond to a `RooFit`_ tutorial, which performs the same task but uses only iminuit and other libraries from the standard Python scientific stack and Scikit-HEP. It can be used as a learning resource or to decide which toolset to use. The list is incomplete. If you would like to see more tutorials, please leave an issue on Github with your request or feel free to make a PR to provide it yourself. .. toctree:: :maxdepth: 1 notebooks/roofit/rf101_basics notebooks/roofit/rf109_chi2residpull iminuit-2.24.0/doc/update_changelog.py0000644000000000000000000000401414332717401014614 0ustar00from pathlib import Path import re import subprocess as subp from pkg_resources import parse_version from pkg_resources.extern.packaging.version import InvalidVersion import datetime import warnings import sys cwd = Path(__file__).parent version = ( subp.check_output([sys.executable, cwd.parent / "version.py"]).strip().decode() ) new_version = parse_version(version) def parse_version_with_fallback(x): try: return parse_version(x) except InvalidVersion: return parse_version("0.0.1") with warnings.catch_warnings(): warnings.simplefilter("ignore") latest_tag = next( iter( sorted( ( parse_version_with_fallback(x) for x in subp.check_output(["git", "tag"]) .decode() .strip() .split("\n") ), reverse=True, ) ) ) with open(cwd / "changelog.rst") as f: content = f.read() # find latest entry m = re.search(r"([0-9]+\.[0-9]+\.[0-9]+) \([^\)]+\)\n-*", content, re.MULTILINE) previous_version = parse_version(m.group(1)) position = m.span(0)[0] # sanity checks assert new_version > previous_version, f"{new_version} > {previous_version}" git_log = re.findall( r"[a-z0-9]+ ([^\n]+ \(#[0-9]+\))", subp.check_output( ["git", "log", "--oneline", f"v{previous_version}..HEAD"] ).decode(), ) today = datetime.date.today() header = f"{new_version} ({today.strftime('%B %d, %Y')})" new_content = f"{header}\n{'-' * len(header)}\n" if git_log: for x in git_log: if x.startswith("[pre-commit.ci]"): continue x = re.sub( "#([0-9]+)", r"`#\1 `_", x ) new_content += f"- {x.capitalize()}\n" else: new_content += "- Minor improvements\n" new_content += "\n" print(new_content, end="") with open(cwd / "changelog.rst", "w") as f: f.write(f"{content[:position]}{new_content}{content[position:]}") iminuit-2.24.0/extern/root/math/minuit2/inc/LinkDef.h0000644000000000000000000000437314332717401017262 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 10/2005 /********************************************************************** * * * Copyright (c) 2005 ROOT Foundation, CERN/PH-SFT * * * **********************************************************************/ #ifdef __CINT__ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; //#pragma link C++ namespace ROOT::Minuit2; #pragma link C++ class ROOT::Minuit2::GenericFunction; #pragma link C++ class ROOT::Minuit2::FCNBase; #pragma link C++ class ROOT::Minuit2::FCNGradientBase; #pragma link C++ class ROOT::Minuit2::FumiliFCNBase; #pragma link C++ class ROOT::Minuit2::Minuit2Minimizer; #pragma link C++ class ROOT::Minuit2::FunctionMinimum; #pragma link C++ class ROOT::Minuit2::MinuitParameter; #pragma link C++ class ROOT::Minuit2::MinosError; #pragma link C++ class ROOT::Minuit2::MnApplication; #pragma link C++ class ROOT::Minuit2::MnMigrad; #pragma link C++ class ROOT::Minuit2::MnMinos; #pragma link C++ class ROOT::Minuit2::MnHesse; #pragma link C++ class ROOT::Minuit2::MnMinimize; #pragma link C++ class ROOT::Minuit2::MnFumiliMinimize; #pragma link C++ class ROOT::Minuit2::MnScan; #pragma link C++ class ROOT::Minuit2::MnContours; #pragma link C++ class ROOT::Minuit2::MnSimplex; #pragma link C++ class ROOT::Minuit2::MnPlot; #pragma link C++ class ROOT::Minuit2::MnUserParameterState; #pragma link C++ class ROOT::Minuit2::MnUserParameters; #pragma link C++ class ROOT::Minuit2::MnStrategy; #pragma link C++ class ROOT::Minuit2::FunctionMinimizer; #pragma link C++ class ROOT::Minuit2::ModularFunctionMinimizer; #pragma link C++ class ROOT::Minuit2::VariableMetricMinimizer; #pragma link C++ class ROOT::Minuit2::SimplexMinimizer; #pragma link C++ class ROOT::Minuit2::CombinedMinimizer; #pragma link C++ class ROOT::Minuit2::ScanMinimizer; #pragma link C++ class ROOT::Minuit2::FumiliMinimizer; #pragma link C++ class ROOT::Minuit2::MnMachinePrecision; #pragma link C++ class ROOT::Minuit2::MnTraceObject; #pragma link C++ class std::vector; #pragma link C++ class TMinuit2TraceObject; #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ABObj.h0000644000000000000000000000763614332717401020217 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ABObj #define ROOT_Minuit2_ABObj #include "Minuit2/ABTypes.h" namespace ROOT { namespace Minuit2 { template class ABObj { public: typedef mtype Type; private: ABObj() : fObject(M()), fFactor(T(0.)) {} ABObj &operator=(const ABObj &) { return *this; } template ABObj(const ABObj &) : fObject(M()), fFactor(T(0.)) { } template ABObj &operator=(const ABObj &) { return *this; } public: ABObj(const M &obj) : fObject(obj), fFactor(T(1.)) {} ABObj(const M &obj, T factor) : fObject(obj), fFactor(factor) {} ~ABObj() {} ABObj(const ABObj &obj) : fObject(obj.fObject), fFactor(obj.fFactor) {} template ABObj(const ABObj &obj) : fObject(M(obj.Obj())), fFactor(T(obj.f())) { } const M &Obj() const { return fObject; } T f() const { return fFactor; } private: M fObject; T fFactor; }; class LAVector; template <> class ABObj { public: typedef vec Type; private: ABObj &operator=(const ABObj &) = delete; public: ABObj(const LAVector &obj) : fObject(obj), fFactor(double(1.)) {} ABObj(const LAVector &obj, double factor) : fObject(obj), fFactor(factor) {} ~ABObj() {} // remove copy constructure to Fix a problem in AIX // should be able to use the compiler generated one // ABObj(const ABObj& obj) : // fObject(obj.fObject), fFactor(obj.fFactor) {} template ABObj(const ABObj &obj) : fObject(obj.fObject), fFactor(double(obj.fFactor)) { } const LAVector &Obj() const { return fObject; } double f() const { return fFactor; } private: const LAVector &fObject; double fFactor; }; class LASymMatrix; template <> class ABObj { public: typedef sym Type; private: ABObj &operator=(const ABObj &) { return *this; } public: ABObj(const LASymMatrix &obj) : fObject(obj), fFactor(double(1.)) {} ABObj(const LASymMatrix &obj, double factor) : fObject(obj), fFactor(factor) {} ~ABObj() {} ABObj(const ABObj &obj) : fObject(obj.fObject), fFactor(obj.fFactor) {} template ABObj(const ABObj &obj) : fObject(obj.fObject), fFactor(double(obj.fFactor)) { } const LASymMatrix &Obj() const { return fObject; } double f() const { return fFactor; } private: const LASymMatrix &fObject; double fFactor; }; // templated scaling operator * template inline ABObj operator*(T f, const M &obj) { return ABObj(obj, f); } // templated operator / template inline ABObj operator/(const M &obj, T f) { return ABObj(obj, T(1.) / f); } // templated unary operator - template inline ABObj operator-(const M &obj) { return ABObj(obj, T(-1.)); } /* // specialization for LAVector inline ABObj operator*(double f, const LAVector& obj) { return ABObj(obj, f); } inline ABObj operator/(const LAVector& obj, double f) { return ABObj(obj, double(1.)/f); } inline ABObj operator-(const LAVector& obj) { return ABObj(obj, double(-1.)); } */ } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ABObj iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ABProd.h0000644000000000000000000000330414332717401020375 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ABProd #define ROOT_Minuit2_ABProd #include "Minuit2/ABObj.h" namespace ROOT { namespace Minuit2 { template class ABProd { private: ABProd() : fA(M1()), fB(M2()) {} ABProd &operator=(const ABProd &) { return *this; } template ABProd &operator=(const ABProd &) { return *this; } public: ABProd(const M1 &a, const M2 &b) : fA(a), fB(b) {} ~ABProd() {} ABProd(const ABProd &prod) : fA(prod.fA), fB(prod.fB) {} template ABProd(const ABProd &prod) : fA(M1(prod.A())), fB(M2(prod.B())) { } const M1 &A() const { return fA; } const M2 &B() const { return fB; } private: M1 fA; M2 fB; }; // ABObj * ABObj template inline ABObj::Type, ABProd, ABObj>, T> operator*(const ABObj &a, const ABObj &b) { return ABObj::Type, ABProd, ABObj>, T>( ABProd, ABObj>(a, b)); } } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ABProd iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ABSum.h0000644000000000000000000000421214332717401020234 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ABSum #define ROOT_Minuit2_ABSum #include "Minuit2/ABObj.h" namespace ROOT { namespace Minuit2 { template class ABSum { private: ABSum() : fA(M1()), fB(M2()) {} ABSum &operator=(const ABSum &) { return *this; } template ABSum &operator=(const ABSum &) { return *this; } public: ABSum(const M1 &a, const M2 &b) : fA(a), fB(b) {} ~ABSum() {} ABSum(const ABSum &sum) : fA(sum.fA), fB(sum.fB) {} template ABSum(const ABSum &sum) : fA(M1(sum.A())), fB(M2(sum.B())) { } const M1 &A() const { return fA; } const M2 &B() const { return fB; } private: M1 fA; M2 fB; }; // ABObj + ABObj template inline ABObj::Type, ABSum, ABObj>, T> operator+(const ABObj &a, const ABObj &b) { return ABObj::Type, ABSum, ABObj>, T>( ABSum, ABObj>(a, b)); } // ABObj - ABObj template inline ABObj::Type, ABSum, ABObj>, T> operator-(const ABObj &a, const ABObj &b) { return ABObj::Type, ABSum, ABObj>, T>( ABSum, ABObj>(a, ABObj(b.Obj(), T(-1.) * b.f()))); } } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ABSum iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ABTypes.h0000644000000000000000000000370714332717401020604 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ABTypes #define ROOT_Minuit2_ABTypes namespace ROOT { namespace Minuit2 { class gen { }; class sym { }; class vec { }; template class AlgebraicSumType { public: typedef gen Type; }; template class AlgebraicSumType { public: typedef T Type; }; template <> class AlgebraicSumType { private: typedef gen Type; }; template <> class AlgebraicSumType { private: typedef gen Type; }; template <> class AlgebraicSumType { private: typedef gen Type; }; template <> class AlgebraicSumType { private: typedef gen Type; }; // template class AlgebraicProdType { private: typedef gen Type; }; template class AlgebraicProdType { private: typedef T Type; }; template <> class AlgebraicProdType { public: typedef gen Type; }; template <> class AlgebraicProdType { public: typedef gen Type; }; template <> class AlgebraicProdType { public: typedef gen Type; }; template <> class AlgebraicProdType { public: typedef gen Type; }; template <> class AlgebraicProdType { private: typedef gen Type; }; template <> class AlgebraicProdType { public: typedef vec Type; }; template <> class AlgebraicProdType { private: typedef gen Type; }; template <> class AlgebraicProdType { public: typedef vec Type; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ABTypes iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/AnalyticalGradientCalculator.h0000644000000000000000000000254014332717401025040 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_AnalyticalGradientCalculator #define ROOT_Minuit2_AnalyticalGradientCalculator #include "Minuit2/GradientCalculator.h" namespace ROOT { namespace Minuit2 { class FCNGradientBase; class MnUserTransformation; class AnalyticalGradientCalculator : public GradientCalculator { public: AnalyticalGradientCalculator(const FCNGradientBase &fcn, const MnUserTransformation &state) : fGradCalc(fcn), fTransformation(state) { } ~AnalyticalGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; virtual bool CheckGradient() const; protected: const FCNGradientBase &fGradCalc; const MnUserTransformation &fTransformation; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_AnalyticalGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/BFGSErrorUpdator.h0000644000000000000000000000201514332717401022356 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_BFGSErrorUpdator #define ROOT_Minuit2_BFGSErrorUpdator #include "Minuit2/MinimumErrorUpdator.h" namespace ROOT { namespace Minuit2 { /** Update of the covariance matrix for the Variable Metric minimizer (MIGRAD) */ class BFGSErrorUpdator : public MinimumErrorUpdator { public: BFGSErrorUpdator() {} ~BFGSErrorUpdator() override {} MinimumError Update(const MinimumState &, const MinimumParameters &, const FunctionGradient &) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_BFGSErrorUpdator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/CombinedMinimizer.h0000644000000000000000000000303514332717401022673 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_CombinedMinimizer #define ROOT_Minuit2_CombinedMinimizer #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/MnSeedGenerator.h" #include "Minuit2/CombinedMinimumBuilder.h" namespace ROOT { namespace Minuit2 { //__________________________________________________________________________ /** Combined minimizer: combination of Migrad and Simplex. I If the Migrad method fails at first attempt, a simplex minimization is performed and then migrad is tried again. */ class CombinedMinimizer : public ModularFunctionMinimizer { public: CombinedMinimizer() : fMinSeedGen(MnSeedGenerator()), fMinBuilder(CombinedMinimumBuilder()) {} ~CombinedMinimizer() override {} const MinimumSeedGenerator &SeedGenerator() const override { return fMinSeedGen; } const MinimumBuilder &Builder() const override { return fMinBuilder; } MinimumBuilder &Builder() override { return fMinBuilder; } private: MnSeedGenerator fMinSeedGen; CombinedMinimumBuilder fMinBuilder; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_CombinedMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/CombinedMinimumBuilder.h0000644000000000000000000000400414332717401023647 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_CombinedMinimumBuilder #define ROOT_Minuit2_CombinedMinimumBuilder #include "Minuit2/MinimumBuilder.h" #include "Minuit2/VariableMetricMinimizer.h" #include "Minuit2/SimplexMinimizer.h" namespace ROOT { namespace Minuit2 { class CombinedMinimumBuilder : public MinimumBuilder { public: CombinedMinimumBuilder() : fVMMinimizer(VariableMetricMinimizer()), fSimplexMinimizer(SimplexMinimizer()) {} ~CombinedMinimumBuilder() override {} FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const override; // re-implement setter of base class. Need also to store in the base class for consistency void SetPrintLevel(int level) override { MinimumBuilder::SetPrintLevel(level); fVMMinimizer.Builder().SetPrintLevel(level); fSimplexMinimizer.Builder().SetPrintLevel(level); } void SetStorageLevel(int level) override { MinimumBuilder::SetStorageLevel(level); fVMMinimizer.Builder().SetStorageLevel(level); fSimplexMinimizer.Builder().SetStorageLevel(level); } // set trace object (user manages it) void SetTraceObject(MnTraceObject &obj) override { MinimumBuilder::SetTraceObject(obj); fVMMinimizer.Builder().SetTraceObject(obj); fSimplexMinimizer.Builder().SetTraceObject(obj); } private: VariableMetricMinimizer fVMMinimizer; SimplexMinimizer fSimplexMinimizer; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_CombinedMinimumBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ContoursError.h0000644000000000000000000000442214332717401022116 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ContoursError #define ROOT_Minuit2_ContoursError #include "Minuit2/MnConfig.h" #include "Minuit2/MinosError.h" #include #include namespace ROOT { namespace Minuit2 { class ContoursError { public: ContoursError(unsigned int parx, unsigned int pary, const std::vector> &points, const MinosError &xmnos, const MinosError &ymnos, unsigned int nfcn) : fParX(parx), fParY(pary), fPoints(points), fXMinos(xmnos), fYMinos(ymnos), fNFcn(nfcn) { } ~ContoursError() {} ContoursError(const ContoursError &cont) : fParX(cont.fParX), fParY(cont.fParY), fPoints(cont.fPoints), fXMinos(cont.fXMinos), fYMinos(cont.fYMinos), fNFcn(cont.fNFcn) { } ContoursError &operator()(const ContoursError &cont) { fParX = cont.fParX; fParY = cont.fParY; fPoints = cont.fPoints; fXMinos = cont.fXMinos; fYMinos = cont.fYMinos; fNFcn = cont.fNFcn; return *this; } const std::vector> &operator()() const { return fPoints; } std::pair XMinos() const { return fXMinos(); } std::pair YMinos() const { return fYMinos(); } unsigned int Xpar() const { return fParX; } unsigned int Ypar() const { return fParY; } const MinosError &XMinosError() const { return fXMinos; } const MinosError &YMinosError() const { return fYMinos; } unsigned int NFcn() const { return fNFcn; } double XMin() const { return fXMinos.Min(); } double YMin() const { return fYMinos.Min(); } private: unsigned int fParX; unsigned int fParY; std::vector> fPoints; MinosError fXMinos; MinosError fYMinos; unsigned int fNFcn; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ContoursError iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/DavidonErrorUpdator.h0000644000000000000000000000203714332717401023225 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_DavidonErrorUpdator #define ROOT_Minuit2_DavidonErrorUpdator #include "Minuit2/MinimumErrorUpdator.h" namespace ROOT { namespace Minuit2 { /** Update of the covariance matrix for the Variable Metric minimizer (MIGRAD) */ class DavidonErrorUpdator : public MinimumErrorUpdator { public: DavidonErrorUpdator() {} ~DavidonErrorUpdator() override {} MinimumError Update(const MinimumState &, const MinimumParameters &, const FunctionGradient &) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_DavidonErrorUpdator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ExternalInternalGradientCalculator.h0000644000000000000000000000353414332717401026242 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ExternalInternalGradientCalculator #define ROOT_Minuit2_ExternalInternalGradientCalculator #include "Minuit2/AnalyticalGradientCalculator.h" namespace ROOT { namespace Minuit2 { class FCNGradientBase; class MnUserTransformation; /// Similar to the AnalyticalGradientCalculator, the ExternalInternalGradientCalculator /// supplies Minuit with an externally calculated gradient. The main difference is that /// ExternalInternalGradientCalculator expects that the external gradient calculator does /// things in Minuit2-internal parameter space, which means many int2ext and ext2int /// transformation steps are not necessary. This avoids loss of precision in some cases, /// where trigonometrically transforming parameters back and forth can lose a few bits of /// floating point precision on every pass. class ExternalInternalGradientCalculator : public AnalyticalGradientCalculator { public: ExternalInternalGradientCalculator(const FCNGradientBase &fcn, const MnUserTransformation &state) : AnalyticalGradientCalculator(fcn, state) { } ~ExternalInternalGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ExternalInternalGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FCNAdapter.h0000644000000000000000000000266414332717401021205 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 10/2006 /********************************************************************** * * * Copyright (c) 2006 ROOT Foundation, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FCNAdapter #define ROOT_Minuit2_FCNAdapter #include "Minuit2/FCNBase.h" #include namespace ROOT { namespace Minuit2 { /** template wrapped class for adapting to FCNBase signature @author Lorenzo Moneta @ingroup Minuit */ template class FCNAdapter : public FCNBase { public: FCNAdapter(const Function &f, double up = 1.) : fFunc(f), fUp(up) {} ~FCNAdapter() override {} double operator()(const std::vector &v) const override { return fFunc.operator()(&v[0]); } double operator()(const double *v) const { return fFunc.operator()(v); } double Up() const override { return fUp; } void SetErrorDef(double up) override { fUp = up; } // virtual std::vector Gradient(const std::vector&) const; // forward interface // virtual double operator()(int npar, double* params,int iflag = 4) const; private: const Function &fFunc; double fUp; }; } // end namespace Minuit2 } // end namespace ROOT #endif // ROOT_Minuit2_FCNAdapter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FCNBase.h0000644000000000000000000000664114332717401020476 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FCNBase #define ROOT_Minuit2_FCNBase #include "Minuit2/MnConfig.h" #include #include "Minuit2/GenericFunction.h" namespace ROOT { namespace Minuit2 { /** \defgroup Minuit Minuit2 Minimization Library New Object-oriented implementation of the MINUIT minimization package. More information is available at the home page of the \ref Minuit2Page "Minuit2" minimization package". \ingroup Math */ //______________________________________________________________________________ /** Interface (abstract class) defining the function to be minimized, which has to be implemented by the user. @author Fred James and Matthias Winkler; modified by Andras Zsenei and Lorenzo Moneta \ingroup Minuit */ class FCNBase : public GenericFunction { public: ~FCNBase() override {} /** The meaning of the vector of parameters is of course defined by the user, who uses the values of those parameters to calculate their function Value. The order and the position of these parameters is strictly the one specified by the user when supplying the starting values for minimization. The starting values must be specified by the user, either via an std::vector or the MnUserParameters supplied as input to the MINUIT minimizers such as VariableMetricMinimizer or MnMigrad. Later values are determined by MINUIT as it searches for the Minimum or performs whatever analysis is requested by the user. @param v function parameters as defined by the user. @return the Value of the function. @see MnUserParameters @see VariableMetricMinimizer @see MnMigrad */ double operator()(const std::vector &v) const override = 0; /** Error definition of the function. MINUIT defines Parameter errors as the change in Parameter Value required to change the function Value by up. Normally, for chisquared fits it is 1, and for negative log likelihood, its Value is 0.5. If the user wants instead the 2-sigma errors for chisquared fits, it becomes 4, as Chi2(x+n*sigma) = Chi2(x) + n*n. Comment a little bit better with links!!!!!!!!!!!!!!!!! */ virtual double ErrorDef() const { return Up(); } /** Error definition of the function. MINUIT defines Parameter errors as the change in Parameter Value required to change the function Value by up. Normally, for chisquared fits it is 1, and for negative log likelihood, its Value is 0.5. If the user wants instead the 2-sigma errors for chisquared fits, it becomes 4, as Chi2(x+n*sigma) = Chi2(x) + n*n. \todo Comment a little bit better with links!!!!!!!!!!!!!!!!! Idem for ErrorDef() */ virtual double Up() const = 0; /** add interface to set dynamically a new error definition Re-implement this function if needed. */ virtual void SetErrorDef(double){}; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FCNBase iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FCNGradAdapter.h0000644000000000000000000000531014332717401021772 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 10/2006 /********************************************************************** * * * Copyright (c) 2006 ROOT Foundation, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FCNGradAdapter #define ROOT_Minuit2_FCNGradAdapter #include "Minuit2/FCNGradientBase.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { /** template wrapped class for adapting to FCNBase signature a IGradFunction @author Lorenzo Moneta @ingroup Minuit */ template class FCNGradAdapter : public FCNGradientBase { public: FCNGradAdapter(const Function &f, double up = 1.) : fFunc(f), fUp(up), fGrad(std::vector(fFunc.NDim())) {} ~FCNGradAdapter() override {} double operator()(const std::vector &v) const override { return fFunc.operator()(&v[0]); } double operator()(const double *v) const { return fFunc.operator()(v); } double Up() const override { return fUp; } std::vector Gradient(const std::vector &v) const override { fFunc.Gradient(&v[0], &fGrad[0]); MnPrint("FCNGradAdapter").Debug([&](std::ostream &os) { os << "gradient in FCNAdapter = {"; for (unsigned int i = 0; i < fGrad.size(); ++i) os << fGrad[i] << (i == fGrad.size() - 1 ? '}' : '\t'); }); return fGrad; } std::vector GradientWithPrevResult(const std::vector &v, double *previous_grad, double *previous_g2, double *previous_gstep) const override { fFunc.GradientWithPrevResult(&v[0], &fGrad[0], previous_grad, previous_g2, previous_gstep); MnPrint("FCNGradAdapter").Debug([&](std::ostream &os) { os << "gradient in FCNAdapter = {"; for (unsigned int i = 0; i < fGrad.size(); ++i) os << fGrad[i] << (i == fGrad.size() - 1 ? '}' : '\t'); }); return fGrad; } // forward interface // virtual double operator()(int npar, double* params,int iflag = 4) const; bool CheckGradient() const override { return false; } GradientParameterSpace gradParameterSpace() const override { if (fFunc.returnsInMinuit2ParameterSpace()) { return GradientParameterSpace::Internal; } else { return GradientParameterSpace::External; } } private: const Function &fFunc; double fUp; mutable std::vector fGrad; }; } // end namespace Minuit2 } // end namespace ROOT #endif // ROOT_Minuit2_FCNGradAdapter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FCNGradientBase.h0000644000000000000000000000407114332717401022147 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FCNGradientBase #define ROOT_Minuit2_FCNGradientBase #include "Minuit2/FCNBase.h" #include namespace ROOT { namespace Minuit2 { //________________________________________________________________________ /** Extension of the FCNBase for providing the analytical Gradient of the function. The user-Gradient is checked at the beginning of the minimization against the Minuit internal numerical Gradient in order to spot problems in the analytical Gradient calculation. This can be turned off by overriding CheckGradient() to make it return "false". The size of the output Gradient vector must be equal to the size of the input Parameter vector. Minuit does a check of the user Gradient at the beginning, if this is not wanted the method "CheckGradient()" has to be overridden to return "false". */ enum class GradientParameterSpace { External, Internal }; class FCNGradientBase : public FCNBase { public: ~FCNGradientBase() override {} virtual std::vector Gradient(const std::vector &) const = 0; virtual std::vector GradientWithPrevResult(const std::vector ¶meters, double * /*previous_grad*/, double * /*previous_g2*/, double * /*previous_gstep*/) const { return Gradient(parameters); }; virtual bool CheckGradient() const { return true; } virtual GradientParameterSpace gradParameterSpace() const { return GradientParameterSpace::External; }; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FCNGradientBase iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliBuilder.h0000644000000000000000000000751614332717401022033 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliBuilder #define ROOT_Minuit2_FumiliBuilder #include "Minuit2/MinimumBuilder.h" #include "Minuit2/VariableMetricEDMEstimator.h" #include "Minuit2/FumiliErrorUpdator.h" #include "Minuit2/MnFcn.h" #include "Minuit2/FunctionMinimum.h" #include namespace ROOT { namespace Minuit2 { /** Builds the FunctionMinimum using the Fumili method. @author Andras Zsenei, Creation date: 29 Sep 2004 @see MINUIT Tutorial on function minimization, section 5 @ingroup Minuit \todo the role of the strategy in Fumili */ class FumiliBuilder : public MinimumBuilder { public: FumiliBuilder() : fEstimator(VariableMetricEDMEstimator()), fErrorUpdator(FumiliErrorUpdator()) {} ~FumiliBuilder() override {} /** Class the member function calculating the Minimum and verifies the result depending on the strategy. @param fMnFcn the function to be minimized. @param fGradienCalculator not used in Fumili. @param fMinimumSeed the seed generator. @param fMnStrategy the strategy describing the number of function calls allowed for Gradient calculations. @param maxfcn maximum number of function calls after which the calculation will be stopped even if it has not yet converged. @param edmval expected vertical distance to the Minimum. @return Returns the function Minimum found. \todo Complete the documentation by understanding what is the reason to have two Minimum methods. */ FunctionMinimum Minimum(const MnFcn &fMnFcn, const GradientCalculator &fGradienCalculator, const MinimumSeed &fMinimumSeed, const MnStrategy &fMnStrategy, unsigned int maxfcn, double edmval) const override; /** Calculates the Minimum based on the Fumili method @param fMnFcn the function to be minimized. @param fGradienCalculator not used in Fumili @param fMinimumSeed the seed generator. @param states vector containing the state result of each iteration @param maxfcn maximum number of function calls after which the calculation will be stopped even if it has not yet converged. @param edmval expected vertical distance to the Minimum @return Returns the function Minimum found. @see MINUIT Tutorial on function minimization, section 5 \todo some nice Latex based formula here... */ FunctionMinimum Minimum(const MnFcn &fMnFcn, const GradientCalculator &fGradienCalculator, const MinimumSeed &fMinimumSeed, std::vector &states, unsigned int maxfcn, double edmval) const; /** Accessor to the EDM (expected vertical distance to the Minimum) estimator. @return The EDM estimator used in the builder. \todo Maybe a little explanation concerning EDM in all relevant classes. */ const VariableMetricEDMEstimator &Estimator() const { return fEstimator; } /** Accessor to the Error updator of the builder. @return The FumiliErrorUpdator used by the FumiliBuilder. */ const FumiliErrorUpdator &ErrorUpdator() const { return fErrorUpdator; } private: VariableMetricEDMEstimator fEstimator; FumiliErrorUpdator fErrorUpdator; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliChi2FCN.h0000644000000000000000000001106114332717401021547 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliChi2FCN #define ROOT_Minuit2_FumiliChi2FCN #include "FumiliFCNBase.h" #include #include "Minuit2/ParametricFunction.h" namespace ROOT { namespace Minuit2 { /** Extension of the FCNBase for the Fumili method. Fumili applies only to minimization problems used for fitting. The method is based on a linearization of the model function negleting second derivatives. User needs to provide the model function. The figure-of-merit describing the difference between the model function and the actual measurements in the case of chi-square is the sum of the squares of the figures-of-merit calculated for each measurement point, which is implemented by the operator() member function. The user still has to implement the calculation of the individual figures-of-merit (which in the majority of the cases will be the (measured Value - the Value predicted by the model)/standard deviation implemeted by the FumiliStandardChi2FCN; however this form can become more complicated (see for an example Numerical Recipes' section on "Straight-Line Data with Errors in Both Coordinates")). @author Andras Zsenei and Lorenzo Moneta, Creation date: 24 Aug 2004 @see MINUIT Tutorial on function minimization, section 5 @see FumiliStandardChi2FCN @ingroup Minuit */ class FumiliChi2FCN : public FumiliFCNBase { public: FumiliChi2FCN() {} ~FumiliChi2FCN() override {} /** Sets the model function for the data (for example gaussian+linear for a peak) @param modelFCN a reference to the model function. */ void SetModelFunction(const ParametricFunction &modelFCN) { fModelFunction = &modelFCN; } /** Returns the model function used for the data. @return Returns a pointer to the model function. */ const ParametricFunction *ModelFunction() const { return fModelFunction; } /** Evaluates the model function for the different measurement points and the Parameter values supplied, calculates a figure-of-merit for each measurement and returns a vector containing the result of this evaluation. @param par vector of Parameter values to feed to the model function. @return A vector containing the figures-of-merit for the model function evaluated for each set of measurements. */ virtual std::vector Elements(const std::vector &par) const = 0; /** Accessor to the parameters of a given measurement. For example in the case of a chi-square fit with a one-dimensional Gaussian, the Parameter characterizing the measurement will be the position. It is the Parameter that is feeded to the model function. @param Index Index of the measueremnt the parameters of which to return @return A reference to a vector containing the values characterizing a measurement */ virtual const std::vector &GetMeasurement(int Index) const = 0; /** Accessor to the number of measurements used for calculating the present figure of merit. @return the number of measurements */ virtual int GetNumberOfMeasurements() const = 0; /** Calculates the sum of Elements squared, ie the chi-square. The user must implement in a class which inherits from FumiliChi2FCN the member function Elements() which will supply the Elements for the sum. @param par vector containing the Parameter values for the model function @return The sum of Elements squared @see FumiliFCNBase#elements */ double operator()(const std::vector &par) const override { double chiSquare = 0.0; std::vector vecElements = Elements(par); unsigned int vecElementsSize = vecElements.size(); for (unsigned int i = 0; i < vecElementsSize; ++i) chiSquare += vecElements[i] * vecElements[i]; return chiSquare; } /** !!!!!!!!!!!! to be commented */ double Up() const override { return 1.0; } private: // A pointer to the model function which describes the data const ParametricFunction *fModelFunction = nullptr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliChi2FCN iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliErrorUpdator.h0000644000000000000000000000603414332717401023067 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliErrorUpdator #define ROOT_Minuit2_FumiliErrorUpdator #include "Minuit2/MinimumErrorUpdator.h" namespace ROOT { namespace Minuit2 { class MinimumState; class MinimumParameters; class GradientCalculator; class FumiliFCNBase; class FunctionGradient; /** In the case of the Fumili algorithm the Error matrix (or the Hessian matrix containing the (approximate) second derivatives) is calculated using a linearization of the model function negleting second derivatives. (In some sense the Name Updator is a little bit misleading as the Error matrix is not calculated by iteratively updating, like in Davidon's or other similar variable metric methods, but by recalculating each time). @author Andras Zsenei and Lorenzo Moneta, Creation date: 28 Sep 2004 @see MINUIT Tutorial on function minimization, section 5 @see DavidonErrorUpdator @ingroup Minuit */ class FumiliErrorUpdator : public MinimumErrorUpdator { public: FumiliErrorUpdator() {} ~FumiliErrorUpdator() override {} /** Member function that calculates the Error matrix (or the Hessian matrix containing the (approximate) second derivatives) using a linearization of the model function negleting second derivatives. @param fMinimumState used to calculate the change in the covariance matrix between the two iterations @param fMinimumParameters the parameters at the present iteration @param fGradientCalculator the Gradient calculator used to retrieved the Parameter transformation @param lambda the Marquard lambda factor \todo Some nice latex mathematical formuli... */ virtual MinimumError Update(const MinimumState &fMinimumState, const MinimumParameters &fMinimumParameters, const GradientCalculator &fGradientCalculator, double lambda) const; /** Member function which is only present due to the design already in place of the software. As all classes calculating the Error matrix are supposed inherit from the MinimumErrorUpdator they must inherit this method. In some methods calculating the aforementioned matrix some of these parameters are not needed and other parameters are necessary... Hopefully, a more elegant solution will be found in the future. \todo How to get rid of this dummy method which is only due to the inheritance */ MinimumError Update(const MinimumState &, const MinimumParameters &, const FunctionGradient &) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliErrorUpdator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliFCNAdapter.h0000644000000000000000000000735714332717401022357 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 10/2006 /********************************************************************** * * * Copyright (c) 2006 ROOT Foundation, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliFCNAdapter #define ROOT_Minuit2_FumiliFCNAdapter #include "Minuit2/FumiliFCNBase.h" #include "Math/FitMethodFunction.h" #include "Minuit2/MnPrint.h" // #ifndef ROOT_Math_Util // #include "Math/Util.h" // #endif #include #include #include namespace ROOT { namespace Minuit2 { /** template wrapped class for adapting to FumiliFCNBase signature @author Lorenzo Moneta @ingroup Minuit */ template class FumiliFCNAdapter : public FumiliFCNBase { public: // typedef ROOT::Math::FitMethodFunction Function; typedef typename Function::Type_t Type_t; FumiliFCNAdapter(const Function &f, unsigned int ndim, double up = 1.) : FumiliFCNBase(ndim), fFunc(f), fUp(up) {} ~FumiliFCNAdapter() override {} double operator()(const std::vector &v) const override { return fFunc.operator()(&v[0]); } double operator()(const double *v) const { return fFunc.operator()(v); } double Up() const override { return fUp; } void SetErrorDef(double up) override { fUp = up; } // virtual std::vector Gradient(const std::vector&) const; // forward interface // virtual double operator()(int npar, double* params,int iflag = 4) const; /** evaluate gradient hessian and function value needed by fumili */ void EvaluateAll(const std::vector &v) override; private: // data member const Function &fFunc; double fUp; }; template void FumiliFCNAdapter::EvaluateAll(const std::vector &v) { MnPrint print("FumiliFCNAdaptor"); // typedef FumiliFCNAdapter::Function Function; // evaluate all elements unsigned int npar = Dimension(); if (npar != v.size()) print.Error("npar", npar, "v.size()", v.size()); assert(npar == v.size()); // must distinguish case of likelihood or LS std::vector &grad = Gradient(); std::vector &hess = Hessian(); // reset assert(grad.size() == npar); grad.assign(npar, 0.0); hess.assign(hess.size(), 0.0); unsigned int ndata = fFunc.NPoints(); std::vector gf(npar); // loop on the data points // assume for now least-square if (fFunc.Type() == Function::kLeastSquare) { for (unsigned int i = 0; i < ndata; ++i) { // calculate data element and gradient double fval = fFunc.DataElement(&v.front(), i, &gf[0]); for (unsigned int j = 0; j < npar; ++j) { grad[j] += 2. * fval * gf[j]; for (unsigned int k = j; k < npar; ++k) { int idx = j + k * (k + 1) / 2; hess[idx] += 2.0 * gf[j] * gf[k]; } } } } else if (fFunc.Type() == Function::kLogLikelihood) { for (unsigned int i = 0; i < ndata; ++i) { // calculate data element and gradient fFunc.DataElement(&v.front(), i, &gf[0]); for (unsigned int j = 0; j < npar; ++j) { double gfj = gf[j]; grad[j] -= gfj; for (unsigned int k = j; k < npar; ++k) { int idx = j + k * (k + 1) / 2; hess[idx] += gfj * gf[k]; } } } } else { print.Error("Type of fit method is not supported, it must be chi2 or log-likelihood"); } } } // end namespace Minuit2 } // end namespace ROOT #endif // ROOT_Minuit2_FCNAdapter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliFCNBase.h0000644000000000000000000001054514332717401021642 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliFCNBase #define ROOT_Minuit2_FumiliFCNBase #include "Minuit2/FCNBase.h" #include #include namespace ROOT { namespace Minuit2 { //____________________________________________________________________________________________ /** Extension of the FCNBase for the Fumili method. Fumili applies only to minimization problems used for fitting. The method is based on a linearization of the model function negleting second derivatives. User needs to provide the model function. The figure-of-merit describing the difference between the model function and the actual measurements has to be implemented by the user in a subclass of FumiliFCNBase. For an example see the FumiliChi2FCN and FumiliStandardChi2FCN classes. @author Andras Zsenei and Lorenzo Moneta, Creation date: 23 Aug 2004 @see MINUIT Tutorial on function minimization, section 5 @see FumiliChi2FCN @see FumiliStandardChi2FCN @ingroup Minuit */ class FumiliFCNBase : public FCNBase { public: /** Default Constructor. Need in this case to create when implementing EvaluateAll the Gradient and Hessian vectors with the right size */ FumiliFCNBase() : fNumberOfParameters(0), fValue(0) {} /** Constructor which initializes the class with the function provided by the user for modeling the data. @param npar the number of parameters */ FumiliFCNBase(unsigned int npar) : fNumberOfParameters(npar), fValue(0), fGradient(std::vector(npar)), fHessian(std::vector(static_cast(0.5 * npar * (npar + 1)))) { } // FumiliFCNBase(const ParametricFunction& modelFCN) { fModelFunction = &modelFCN; } ~FumiliFCNBase() override {} /** Evaluate function Value, Gradient and Hessian using Fumili approximation, for values of parameters p The resul is cached inside and is return from the FumiliFCNBase::Value , FumiliFCNBase::Gradient and FumiliFCNBase::Hessian methods @param par vector of parameters **/ virtual void EvaluateAll(const std::vector &par) = 0; /** Return cached Value of objective function estimated previously using the FumiliFCNBase::EvaluateAll method **/ virtual double Value() const { return fValue; } /** Return cached Value of function Gradient estimated previously using the FumiliFCNBase::EvaluateAll method **/ virtual const std::vector &Gradient() const { return fGradient; } /** Return Value of the i-th j-th element of the Hessian matrix estimated previously using the FumiliFCNBase::EvaluateAll method @param row row Index of the matrix @param col col Index of the matrix **/ virtual double Hessian(unsigned int row, unsigned int col) const { assert(row < fGradient.size() && col < fGradient.size()); if (row > col) return fHessian[col + row * (row + 1) / 2]; else return fHessian[row + col * (col + 1) / 2]; } /** return number of function variable (parameters) , i.e. function dimension */ virtual unsigned int Dimension() { return fNumberOfParameters; } protected: /** initialize and reset values of gradien and Hessian */ virtual void InitAndReset(unsigned int npar) { fNumberOfParameters = npar; fGradient = std::vector(npar); fHessian = std::vector(static_cast(0.5 * npar * (npar + 1))); } // methods to be used by the derived classes to set the values void SetFCNValue(double value) { fValue = value; } std::vector &Gradient() { return fGradient; } std::vector &Hessian() { return fHessian; } private: unsigned int fNumberOfParameters; double fValue; std::vector fGradient; std::vector fHessian; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliFCNBase iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliGradientCalculator.h0000644000000000000000000000301714332717401024204 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliGradientCalculator #define ROOT_Minuit2_FumiliGradientCalculator #include "Minuit2/GradientCalculator.h" #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { class FumiliFCNBase; class MnUserTransformation; class FumiliGradientCalculator : public GradientCalculator { public: FumiliGradientCalculator(const FumiliFCNBase &fcn, const MnUserTransformation &state, int n) : fFcn(fcn), fTransformation(state), fHessian(MnAlgebraicSymMatrix(n)) { } ~FumiliGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; const MnUserTransformation &Trafo() const { return fTransformation; } const MnAlgebraicSymMatrix &Hessian() const { return fHessian; } private: const FumiliFCNBase &fFcn; const MnUserTransformation &fTransformation; mutable MnAlgebraicSymMatrix fHessian; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliMaximumLikelihoodFCN.h0000644000000000000000000001065114332717401024407 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliMaximumLikelihoodFCN #define ROOT_Minuit2_FumiliMaximumLikelihoodFCN #include "FumiliFCNBase.h" #include "Minuit2/ParametricFunction.h" #include "Math/Util.h" #include #include namespace ROOT { namespace Minuit2 { /** Extension of the FCNBase for the Fumili method. Fumili applies only to minimization problems used for fitting. The method is based on a linearization of the model function negleting second derivatives. User needs to provide the model function. In this cased the function to be minimized is the sum of the logarithms of the model function for the different measurements times -1. @author Andras Zsenei and Lorenzo Moneta, Creation date: 3 Sep 2004 @see MINUIT Tutorial on function minimization, section 5 @see FumiliStandardMaximumLikelihoodFCN @ingroup Minuit \todo Insert a nice latex formula... */ class FumiliMaximumLikelihoodFCN : public FumiliFCNBase { public: FumiliMaximumLikelihoodFCN() {} ~FumiliMaximumLikelihoodFCN() override {} /** Sets the model function for the data (for example gaussian+linear for a peak) @param modelFCN a reference to the model function. */ void SetModelFunction(const ParametricFunction &modelFCN) { fModelFunction = &modelFCN; } /** Returns the model function used for the data. @return Returns a pointer to the model function. */ const ParametricFunction *ModelFunction() const { return fModelFunction; } /** Evaluates the model function for the different measurement points and the Parameter values supplied, calculates a figure-of-merit for each measurement and returns a vector containing the result of this evaluation. @param par vector of Parameter values to feed to the model function. @return A vector containing the figures-of-merit for the model function evaluated for each set of measurements. */ virtual std::vector Elements(const std::vector &par) const = 0; /** Accessor to the parameters of a given measurement. For example in the case of a chi-square fit with a one-dimensional Gaussian, the Parameter characterizing the measurement will be the position. It is the Parameter that is feeded to the model function. @param Index Index of the measueremnt the parameters of which to return @return A vector containing the values characterizing a measurement */ virtual const std::vector &GetMeasurement(int Index) const = 0; /** Accessor to the number of measurements used for calculating the present figure of merit. @return the number of measurements */ virtual int GetNumberOfMeasurements() const = 0; /** Calculates the function for the maximum likelihood method. The user must implement in a class which inherits from FumiliChi2FCN the member function Elements() which will supply the Elements for the sum. @param par vector containing the Parameter values for the model function @return The sum of the natural logarithm of the Elements multiplied by -1 @see FumiliFCNBase#elements */ double operator()(const std::vector &par) const override { double sumoflogs = 0.0; std::vector vecElements = Elements(par); unsigned int vecElementsSize = vecElements.size(); for (unsigned int i = 0; i < vecElementsSize; ++i) { double tmp = vecElements[i]; // for max likelihood probability have to be positive assert(tmp >= 0); sumoflogs -= ROOT::Math::Util::EvalLog(tmp); // std::cout << " i " << tmp << " lik " << sumoflogs << std::endl; } return sumoflogs; } /** !!!!!!!!!!!! to be commented */ double Up() const override { return 0.5; } private: // A pointer to the model function which describes the data const ParametricFunction *fModelFunction; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliMaximumLikelihoodFCN iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliMinimizer.h0000644000000000000000000001340214332717401022377 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliMinimizer #define ROOT_Minuit2_FumiliMinimizer #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/MnSeedGenerator.h" #include "Minuit2/FumiliBuilder.h" #include namespace ROOT { namespace Minuit2 { class MinimumSeedGenerator; class MinimumBuilder; class MinimumSeed; class MnFcn; class FumiliFcnBase; class GradientCalculator; class MnUserParameterState; class MnUserParameters; class MnUserCovariance; class MnStrategy; //______________________________________________________________ /** Instantiates the seed generator and Minimum builder for the Fumili minimization method. Produces the Minimum via the Minimize methods inherited from ModularFunctionMinimizer. @author Andras Zsenei and Lorenzo Moneta, Creation date: 28 Sep 2004 @ingroup Minuit */ class FumiliMinimizer : public ModularFunctionMinimizer { public: /** Constructor initializing the FumiliMinimizer by instantiatiating the SeedGenerator and MinimumBuilder for the Fumili minimization method. @see MnSeedGenerator @see FumiliBuilder */ FumiliMinimizer() : fMinSeedGen(MnSeedGenerator()), fMinBuilder(FumiliBuilder()) {} ~FumiliMinimizer() override {} /** Accessor to the seed generator of the minimizer. @return A reference to the seed generator used by the minimizer */ const MinimumSeedGenerator &SeedGenerator() const override { return fMinSeedGen; } /** Accessor to the Minimum builder of the minimizer. @return a reference to the Minimum builder. */ const FumiliBuilder &Builder() const override { return fMinBuilder; } FumiliBuilder &Builder() override { return fMinBuilder; } // for Fumili FunctionMinimum Minimize(const FCNBase &, const MnUserParameterState &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const override; FunctionMinimum Minimize(const FCNGradientBase &, const MnUserParameterState &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const override; // need to re-implement all function in ModularFuncitionMinimizer otherwise they will be hided FunctionMinimum Minimize(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, err, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNGradientBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, err, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, nrow, cov, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNGradientBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, nrow, cov, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNBase &fcn, const MnUserParameters &par, const MnStrategy &stra, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNGradientBase &fcn, const MnUserParameters &par, const MnStrategy &stra, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, const MnStrategy &stra, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, cov, stra, maxfcn, toler); } FunctionMinimum Minimize(const FCNGradientBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, const MnStrategy &stra, unsigned int maxfcn = 0, double toler = 0.1) const override { return ModularFunctionMinimizer::Minimize(fcn, par, cov, stra, maxfcn, toler); } FunctionMinimum Minimize(const MnFcn &mfcn, const GradientCalculator &gc, const MinimumSeed &seed, const MnStrategy &stra, unsigned int maxfcn, double toler) const override { return ModularFunctionMinimizer::Minimize(mfcn, gc, seed, stra, maxfcn, toler); } private: MnSeedGenerator fMinSeedGen; FumiliBuilder fMinBuilder; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliStandardChi2FCN.h0000644000000000000000000001302514332717401023232 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliStandardChi2FCN #define ROOT_Minuit2_FumiliStandardChi2FCN #include "Minuit2/FumiliChi2FCN.h" #include "Minuit2/ParametricFunction.h" #include #include #include namespace ROOT { namespace Minuit2 { /** Class implementing the standard chi square function, which is the sum of the squares of the figures-of-merit calculated for each measurement point, the individual figures-of-merit being: (the Value predicted by the model-measured Value)/standard deviation. @author Andras Zsenei and Lorenzo Moneta, Creation date: 31 Aug 2004 @see FumiliChi2FCN @ingroup Minuit \todo nice formula for the documentation... */ class FumiliStandardChi2FCN : public FumiliChi2FCN { public: /** Constructor which initializes chi square function for one-dimensional model function @param modelFCN the model function used for describing the data. @param meas vector containing the measured values. @param pos vector containing the x values corresponding to the measurements @param mvar vector containing the variances corresponding to each measurement (where the variance equals the standard deviation squared). If the variances are zero, a Value of 1 is used (as it is done in ROOT/PAW) */ FumiliStandardChi2FCN(const ParametricFunction &modelFCN, const std::vector &meas, const std::vector &pos, const std::vector &mvar) { // this->fModelFCN = &modelFunction; this->SetModelFunction(modelFCN); assert(meas.size() == pos.size()); assert(meas.size() == mvar.size()); fMeasurements = meas; std::vector x(1); unsigned int n = mvar.size(); fPositions.reserve(n); // correct for variance == 0 fInvErrors.resize(n); for (unsigned int i = 0; i < n; ++i) { x[0] = pos[i]; fPositions.push_back(x); // PAW/ROOT hack : use 1 for 0 entries bins if (mvar[i] == 0) fInvErrors[i] = 1; else fInvErrors[i] = 1.0 / std::sqrt(mvar[i]); } } /** Constructor which initializes the multi-dimensional model function. @param modelFCN the model function used for describing the data. @param meas vector containing the measured values. @param pos vector containing the x values corresponding to the measurements @param mvar vector containing the variances corresponding to each measurement (where the variance equals the standard deviation squared). If the variances are zero, a Value of 1 is used (as it is done in ROOT/PAW) */ FumiliStandardChi2FCN(const ParametricFunction &modelFCN, const std::vector &meas, const std::vector> &pos, const std::vector &mvar) { // this->fModelFCN = &modelFunction; this->SetModelFunction(modelFCN); assert(meas.size() == pos.size()); assert(meas.size() == mvar.size()); fMeasurements = meas; fPositions = pos; // correct for variance == 0 unsigned int n = mvar.size(); fInvErrors.resize(n); for (unsigned int i = 0; i < n; ++i) { // PAW/ROOT hack : use 1 for 0 entries bins if (mvar[i] == 0) fInvErrors[i] = 1; else fInvErrors[i] = 1.0 / std::sqrt(mvar[i]); } } ~FumiliStandardChi2FCN() override {} /** Evaluates the model function for the different measurement points and the Parameter values supplied, calculates a figure-of-merit for each measurement and returns a vector containing the result of this evaluation. The figure-of-merit is (Value predicted by the model function-measured Value)/standard deviation. @param par vector of Parameter values to feed to the model function. @return A vector containing the figures-of-merit for the model function evaluated for each set of measurements. \todo What to do when the variances are 0???!! (right now just pushes back 0...) */ std::vector Elements(const std::vector &par) const override; /** Accessor to the position of the measurement (x coordinate). @param Index Index of the measuerement the position of which to return. @return the position of the measurement. */ const std::vector &GetMeasurement(int Index) const override; /** Accessor to the number of measurements used for calculating the chi-square. @return the number of measurements. */ int GetNumberOfMeasurements() const override; /** Evaluate function Value, Gradient and Hessian using Fumili approximation, for values of parameters p The resul is cached inside and is return from the FumiliFCNBase::Value , FumiliFCNBase::Gradient and FumiliFCNBase::Hessian methods @param par vector of parameters **/ void EvaluateAll(const std::vector &par) override; private: std::vector fMeasurements; // support multi dim coordinates std::vector> fPositions; std::vector fInvErrors; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliStandardChi2FCN iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FumiliStandardMaximumLikelihoodFCN.h0000644000000000000000000000670614332717401026076 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FumiliStandardMaximumLikelihoodFCN #define ROOT_Minuit2_FumiliStandardMaximumLikelihoodFCN #include "Minuit2/FumiliMaximumLikelihoodFCN.h" #include "Minuit2/ParametricFunction.h" #include namespace ROOT { namespace Minuit2 { /** Class implementing the Elements member function for the standard maximum likelihood method. @author Andras Zsenei and Lorenzo Moneta, Creation date: 4 Sep 2004 @see FumiliMaximumLikelihoodFCN @ingroup Minuit */ class FumiliStandardMaximumLikelihoodFCN : public FumiliMaximumLikelihoodFCN { public: /** Constructor which initializes the measurement points for the one dimensional model function. @param modelFCN the model function used for describing the data. @param pos vector containing the x values corresponding to the measurements */ FumiliStandardMaximumLikelihoodFCN(const ParametricFunction &modelFCN, const std::vector &pos) { this->SetModelFunction(modelFCN); unsigned int n = pos.size(); fPositions.reserve(n); std::vector x(1); for (unsigned int i = 0; i < n; ++i) { x[0] = pos[i]; fPositions.push_back(x); } } /** Constructor which initializes the measurement points for the multi dimensional model function. @param modelFCN the model function used for describing the data. @param pos vector containing the x values corresponding to the measurements */ FumiliStandardMaximumLikelihoodFCN(const ParametricFunction &modelFCN, const std::vector> &pos) { this->SetModelFunction(modelFCN); fPositions = pos; } ~FumiliStandardMaximumLikelihoodFCN() override {} /** Evaluates the model function for the different measurement points and the Parameter values supplied. @param par vector of Parameter values to feed to the model function. @return A vector containing the model function evaluated for each measurement point. */ std::vector Elements(const std::vector &par) const override; /** Accessor to the position of the measurement (x coordinate). @param Index Index of the measuerement the position of which to return. @return the position of the measurement. */ const std::vector &GetMeasurement(int Index) const override; /** Accessor to the number of measurements used for calculating the maximum likelihood. @return the number of measurements. */ int GetNumberOfMeasurements() const override; /** Evaluate function Value, Gradient and Hessian using Fumili approximation, for values of parameters p The resul is cached inside and is return from the FumiliFCNBase::Value , FumiliFCNBase::Gradient and FumiliFCNBase::Hessian methods @param par vector of parameters **/ void EvaluateAll(const std::vector &par) override; private: std::vector> fPositions; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FumiliStandardMaximumLikelihoodFCN iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FunctionGradient.h0000644000000000000000000000350314332717401022532 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FunctionGradient #define ROOT_Minuit2_FunctionGradient #include "Minuit2/MnMatrix.h" #include namespace ROOT { namespace Minuit2 { class FunctionGradient { private: public: explicit FunctionGradient(unsigned int n) : fPtr{new Data{MnAlgebraicVector(n), MnAlgebraicVector(n), MnAlgebraicVector(n), false, false}} { } explicit FunctionGradient(const MnAlgebraicVector &grd) : fPtr{new Data{grd, MnAlgebraicVector(grd.size()), MnAlgebraicVector(grd.size()), true, true}} { } FunctionGradient(const MnAlgebraicVector &grd, const MnAlgebraicVector &g2, const MnAlgebraicVector &gstep) : fPtr{new Data{grd, g2, gstep, true, false}} { } const MnAlgebraicVector &Grad() const { return fPtr->fGradient; } const MnAlgebraicVector &Vec() const { return Grad(); } bool IsValid() const { return fPtr->fValid; } bool IsAnalytical() const { return fPtr->fAnalytical; } const MnAlgebraicVector &G2() const { return fPtr->fG2ndDerivative; } const MnAlgebraicVector &Gstep() const { return fPtr->fGStepSize; } private: struct Data { MnAlgebraicVector fGradient; MnAlgebraicVector fG2ndDerivative; MnAlgebraicVector fGStepSize; bool fValid; bool fAnalytical; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FunctionGradient iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FunctionMinimizer.h0000644000000000000000000000524314332717401022743 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FunctionMinimizer #define ROOT_Minuit2_FunctionMinimizer #include "Minuit2/MnConfig.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; class FCNGradientBase; class FunctionMinimum; //_____________________________________________________________________________________ /** base class for function minimizers; user may give FCN or FCN with Gradient, Parameter starting values and initial Error guess (sigma) (or "step size"), or Parameter starting values and initial covariance matrix; covariance matrix is stored in Upper triangular packed storage format, e.g. the Elements in the array are arranged like {a(0,0), a(0,1), a(1,1), a(0,2), a(1,2), a(2,2), ...}, the size is nrow*(nrow+1)/2 (see also MnUserCovariance.h); */ class FunctionMinimizer { public: virtual ~FunctionMinimizer() {} // starting values for parameters and errors virtual FunctionMinimum Minimize(const FCNBase &, const std::vector &par, const std::vector &err, unsigned int strategy, unsigned int maxfcn, double toler) const = 0; // starting values for parameters and errors and FCN with Gradient virtual FunctionMinimum Minimize(const FCNGradientBase &, const std::vector &par, const std::vector &err, unsigned int strategy, unsigned int maxfcn, double toler) const = 0; // starting values for parameters and covariance matrix virtual FunctionMinimum Minimize(const FCNBase &, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int strategy, unsigned int maxfcn, double toler) const = 0; // starting values for parameters and covariance matrix and FCN with Gradient virtual FunctionMinimum Minimize(const FCNGradientBase &, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int strategy, unsigned int maxfcn, double toler) const = 0; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FunctionMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/FunctionMinimum.h0000644000000000000000000001157214332717401022415 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_FunctionMinimum #define ROOT_Minuit2_FunctionMinimum #include "Minuit2/MinimumSeed.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnUserTransformation.h" #include #include #include #ifdef G__DICTIONARY typedef ROOT::Minuit2::MinimumState MinimumState; #endif namespace ROOT { namespace Minuit2 { //______________________________________________________________________________________________ /** class holding the full result of the minimization; both internal and external (MnUserParameterState) representation available for the parameters at the Minimum */ class FunctionMinimum { public: enum Status { MnValid, MnReachedCallLimit, MnAboveMaxEdm, }; public: /// Constructor from only MinimumSeed. Minimum is only from seed result not the full minimization FunctionMinimum(const MinimumSeed &seed, double up) : FunctionMinimum(seed, std::vector(1, MinimumState(seed.Parameters(), seed.Error(), seed.Gradient(), seed.Parameters().Fval(), seed.NFcn())), up) { } /// Constructor at the end of a minimization from seed and vector of states FunctionMinimum(const MinimumSeed &seed, const std::vector &states, double up, Status status = MnValid) : fPtr{new Data{seed, states, up, status == MnAboveMaxEdm, status == MnReachedCallLimit, {}}} { } /// add latest minimization state (for example add Hesse result after Migrad) void Add(const MinimumState &state, Status status = MnValid) { fPtr->fStates.push_back(state); // LM : update also the user state fPtr->fUserState = MnUserParameterState(State(), Up(), Seed().Trafo()); // reset maxedm flag. If new state has edm over max other method must be used fPtr->fAboveMaxEdm = status == MnAboveMaxEdm; fPtr->fReachedCallLimit = status == MnReachedCallLimit; } const MinimumSeed &Seed() const { return fPtr->fSeed; } const std::vector &States() const { return fPtr->fStates; } // user representation of state at Minimum const MnUserParameterState &UserState() const { if (!fPtr->fUserState.IsValid()) fPtr->fUserState = MnUserParameterState(State(), Up(), Seed().Trafo()); return fPtr->fUserState; } const MnUserParameters &UserParameters() const { return UserState().Parameters(); } const MnUserCovariance &UserCovariance() const { return UserState().Covariance(); } // forward interface of last state const MinimumState &State() const { return States().back(); } const MinimumParameters &Parameters() const { return States().back().Parameters(); } const MinimumError &Error() const { return States().back().Error(); } const FunctionGradient &Grad() const { return States().back().Gradient(); } double Fval() const { return States().back().Fval(); } double Edm() const { return States().back().Edm(); } int NFcn() const { return States().back().NFcn(); } double Up() const { return fPtr->fErrorDef; } bool IsValid() const { return State().IsValid() && !IsAboveMaxEdm() && !HasReachedCallLimit(); } bool HasValidParameters() const { return State().Parameters().IsValid(); } bool HasValidCovariance() const { return State().Error().IsValid(); } bool HasAccurateCovar() const { return State().Error().IsAccurate(); } bool HasPosDefCovar() const { return State().Error().IsPosDef(); } bool HasMadePosDefCovar() const { return State().Error().IsMadePosDef(); } bool HesseFailed() const { return State().Error().HesseFailed(); } bool HasCovariance() const { return State().Error().IsAvailable(); } bool IsAboveMaxEdm() const { return fPtr->fAboveMaxEdm || std::isnan(Edm()); } bool HasReachedCallLimit() const { return fPtr->fReachedCallLimit; } void SetErrorDef(double up) { fPtr->fErrorDef = up; // update user state for new valeu of up (scaling of errors) fPtr->fUserState = MnUserParameterState(State(), up, Seed().Trafo()); } private: struct Data { MinimumSeed fSeed; std::vector fStates; double fErrorDef; bool fAboveMaxEdm; bool fReachedCallLimit; mutable MnUserParameterState fUserState; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_FunctionMinimum iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/GenericFunction.h0000644000000000000000000000264014332717401022352 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_GenericFunction #define ROOT_Minuit2_GenericFunction #include "Minuit2/MnConfig.h" #include namespace ROOT { namespace Minuit2 { //_____________________________________________________________________ /** Class from which all the other classes, representing functions, inherit. That is why it defines only one method, the operator(), which allows to call the function. @author Andras Zsenei and Lorenzo Moneta, Creation date: 23 Sep 2004 @ingroup Minuit */ class GenericFunction { public: virtual ~GenericFunction() {} /** Evaluates the function using the vector containing the input values. @param x vector of the coordinates (for example the x coordinate for a one-dimensional Gaussian) @return the result of the evaluation of the function. */ virtual double operator()(const std::vector &x) const = 0; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_GenericFunction iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/GradientCalculator.h0000644000000000000000000000177614332717401023050 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_GradientCalculator #define ROOT_Minuit2_GradientCalculator namespace ROOT { namespace Minuit2 { class MinimumParameters; class FunctionGradient; /** interface class for gradient calculators */ class GradientCalculator { public: virtual ~GradientCalculator() {} virtual FunctionGradient operator()(const MinimumParameters &) const = 0; virtual FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const = 0; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_GradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/HessianGradientCalculator.h0000644000000000000000000000366314332717401024360 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_HessianGradientCalculator #define ROOT_Minuit2_HessianGradientCalculator #include "Minuit2/GradientCalculator.h" #include "Minuit2/MnMatrix.h" #include namespace ROOT { namespace Minuit2 { class MnFcn; class MnUserTransformation; class MnMachinePrecision; class MnStrategy; /** HessianGradientCalculator: class to calculate Gradient for Hessian */ class HessianGradientCalculator : public GradientCalculator { public: HessianGradientCalculator(const MnFcn &fcn, const MnUserTransformation &par, const MnStrategy &stra) : fFcn(fcn), fTransformation(par), fStrategy(stra) { } ~HessianGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; std::pair DeltaGradient(const MinimumParameters &, const FunctionGradient &) const; const MnFcn &Fcn() const { return fFcn; } const MnUserTransformation &Trafo() const { return fTransformation; } const MnMachinePrecision &Precision() const; const MnStrategy &Strategy() const { return fStrategy; } unsigned int Ncycle() const; double StepTolerance() const; double GradTolerance() const; private: const MnFcn &fFcn; const MnUserTransformation &fTransformation; const MnStrategy &fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_HessianGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/InitialGradientCalculator.h0000644000000000000000000000335614332717401024356 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_InitialGradientCalculator #define ROOT_Minuit2_InitialGradientCalculator #include "Minuit2/GradientCalculator.h" namespace ROOT { namespace Minuit2 { class MnFcn; class MnUserTransformation; class MnMachinePrecision; class MnStrategy; /** Class to calculate an initial estimate of the gradient */ class InitialGradientCalculator : public GradientCalculator { public: InitialGradientCalculator(const MnFcn &fcn, const MnUserTransformation &par, const MnStrategy &stra) : fFcn(fcn), fTransformation(par), fStrategy(stra){}; ~InitialGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; const MnFcn &Fcn() const { return fFcn; } const MnUserTransformation &Trafo() const { return fTransformation; } const MnMachinePrecision &Precision() const; const MnStrategy &Strategy() const { return fStrategy; } unsigned int Ncycle() const; double StepTolerance() const; double GradTolerance() const; private: const MnFcn &fFcn; const MnUserTransformation &fTransformation; const MnStrategy &fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_InitialGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/LASymMatrix.h0000644000000000000000000003340114332717401021441 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_LASymMatrix #define ROOT_Minuit2_LASymMatrix #include "Minuit2/MnConfig.h" #include "Minuit2/ABSum.h" #include "Minuit2/VectorOuterProduct.h" #include "Minuit2/MatrixInverse.h" #include "Minuit2/StackAllocator.h" #include #include #include // for memcopy // extern StackAllocator StackAllocatorHolder::Get(); namespace ROOT { namespace Minuit2 { int Mndaxpy(unsigned int, double, const double *, int, double *, int); int Mndscal(unsigned int, double, double *, int); class LAVector; int Invert(LASymMatrix &); /** Class describing a symmetric matrix of size n. The size is specified as a run-time argument passed in the constructor. The class uses expression templates for the operations and functions. Only the independent data are kept in the fdata array of size n*(n+1)/2 containing the lower triangular data */ class LASymMatrix { private: LASymMatrix() : fSize(0), fNRow(0), fData(0) {} public: typedef sym Type; LASymMatrix(unsigned int n) : fSize(n * (n + 1) / 2), fNRow(n), fData((double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * n * (n + 1) / 2)) { // assert(fSize>0); std::memset(fData, 0, fSize * sizeof(double)); // std::cout<<"LASymMatrix(unsigned int n), n= "<& v)"< LASymMatrix(const ABObj, ABObj>, T> &sum) : fSize(0), fNRow(0), fData(0) { // std::cout<<"template LASymMatrix(const ABObj, // ABObj > >& sum)"< LASymMatrix(const ABObj..."< LASymMatrix(const ABObj, ABObj>, T> &sum) : fSize(0), fNRow(0), fData(0) { // std::cout<<"template LASymMatrix(const ABObj, // ABObj >,T>& sum)"< LASymMatrix(const ABObj LASymMatrix(const ABObj, T> &something) : fSize(0), fNRow(0), fData(0) { // std::cout<<"template LASymMatrix(const ABObj, T>& // something)"< LASymMatrix(const ABObj, T>& // something)"< LASymMatrix(const ABObj, T>, T> &inv) : fSize(inv.Obj().Obj().Obj().size()), fNRow(inv.Obj().Obj().Obj().Nrow()), fData((double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * inv.Obj().Obj().Obj().size())) { std::memcpy(fData, inv.Obj().Obj().Obj().Data(), fSize * sizeof(double)); Mndscal(fSize, double(inv.Obj().Obj().f()), fData, 1); Invert(*this); Mndscal(fSize, double(inv.f()), fData, 1); } template LASymMatrix( const ABObj, T>, T>, ABObj>, T> &sum) : fSize(0), fNRow(0), fData(0) { // std::cout<<"template LASymMatrix(const ABObj, T>, T>, ABObj >, T>& sum)"< LASymMatrix(const ABObj, double>, double> &); template LASymMatrix( const ABObj, T>, T>, ABObj>, T> &sum) : fSize(0), fNRow(0), fData(0) { // std::cout<<"template LASymMatrix(const ABObj, T>, T> ABObj >,T>& sum)"< LASymMatrix(const ABObj LASymMatrix &operator+=(const ABObj &m) { // std::cout<<"template LASymMatrix& operator+=(const ABObj& m)"< LASymMatrix &operator+=(const ABObj &m) { // std::cout<<"template LASymMatrix& operator+=(const ABObj& m)"< LASymMatrix &operator+=(const ABObj, T>, T> &m) { // std::cout<<"template LASymMatrix& operator+=(const ABObj, T>, T>& m)"< 0); LASymMatrix tmp(m.Obj().Obj()); Invert(tmp); tmp *= double(m.f()); (*this) += tmp; return *this; } template LASymMatrix &operator+=(const ABObj, T>, T> &m) { // std::cout<<"template LASymMatrix& operator+=(const ABObj, T>, T>&"< 0); Outer_prod(*this, m.Obj().Obj().Obj(), m.f() * m.Obj().Obj().f() * m.Obj().Obj().f()); return *this; } LASymMatrix &operator*=(double scal) { Mndscal(fSize, scal, fData, 1); return *this; } double operator()(unsigned int row, unsigned int col) const { assert(row < fNRow && col < fNRow); if (row > col) return fData[col + row * (row + 1) / 2]; else return fData[row + col * (col + 1) / 2]; } double &operator()(unsigned int row, unsigned int col) { assert(row < fNRow && col < fNRow); if (row > col) return fData[col + row * (row + 1) / 2]; else return fData[row + col * (col + 1) / 2]; } const double *Data() const { return fData; } double *Data() { return fData; } unsigned int size() const { return fSize; } unsigned int Nrow() const { return fNRow; } unsigned int Ncol() const { return Nrow(); } private: unsigned int fSize; unsigned int fNRow; double *fData; public: template LASymMatrix &operator=(const ABObj &v) { // std::cout<<"template LASymMatrix& operator=(ABObj& v)"< LASymMatrix &operator=(const ABObj, T> &something) { // std::cout<<"template LASymMatrix& operator=(const ABObj, T>& // something)"< LASymMatrix& operator=(const ABObj, T>& // something)"< LASymMatrix &operator=(const ABObj, ABObj>, T> &sum) { // std::cout<<"template LASymMatrix& operator=(const ABObj, // ABObj >,T>& sum)"< LASymMatrix &operator=(const ABObj, ABObj>, T> &sum) { // std::cout<<"template LASymMatrix& operator=(const ABObj, ABObj >,T>& sum)"< LASymMatrix &operator=(const ABObj, T>, T> &inv) { if (fSize == 0 && fData == 0) { fSize = inv.Obj().Obj().Obj().size(); fNRow = inv.Obj().Obj().Obj().Nrow(); fData = (double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * fSize); std::memcpy(fData, inv.Obj().Obj().Obj().Data(), fSize * sizeof(double)); (*this) *= inv.Obj().Obj().f(); Invert(*this); (*this) *= inv.f(); } else { LASymMatrix tmp(inv.Obj().Obj()); Invert(tmp); tmp *= double(inv.f()); assert(fSize == tmp.size()); std::memcpy(fData, tmp.Data(), fSize * sizeof(double)); } return *this; } LASymMatrix &operator=(const ABObj, double>, double> &); }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_LASymMatrix iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/LAVector.h0000644000000000000000000002541114332717401020750 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_LAVector #define ROOT_Minuit2_LAVector #include "Minuit2/ABSum.h" #include "Minuit2/ABProd.h" #include "Minuit2/LASymMatrix.h" #include #include #include "Minuit2/StackAllocator.h" namespace ROOT { namespace Minuit2 { // extern StackAllocator StackAllocatorHolder::Get(); int Mndaxpy(unsigned int, double, const double *, int, double *, int); int Mndscal(unsigned int, double, double *, int); int Mndspmv(const char *, unsigned int, double, const double *, const double *, int, double, double *, int); class LAVector { private: LAVector() : fSize(0), fData(0) {} public: typedef vec Type; // LAVector() : fSize(0), fData(0) {} LAVector(unsigned int n) : fSize(n), fData((double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * n)) { // assert(fSize>0); std::memset(fData, 0, size() * sizeof(double)); // std::cout<<"LAVector(unsigned int n), n= "<& v)"< LAVector(const ABObj, ABObj>, T> &sum) : fSize(0), fData(0) { // std::cout<<"template LAVector(const ABObj, ABObj > >& // sum)"< LAVector(const ABObj, ABObj>, T> &sum) : fSize(0), fData(0) { // std::cout<<"template LAVector(const ABObj, ABObj >,T>& // sum)"< LAVector(const ABObj LAVector(const ABObj, T> &something) : fSize(0), fData(0) { // std::cout<<"template LAVector(const ABObj, T>& something)"< LAVector(const ABObj, ABObj>, T> &prod) : fSize(prod.Obj().B().Obj().size()), fData((double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * prod.Obj().B().Obj().size())) { // std::cout<<"template LAVector(const ABObj, ABObj >, T>& prod)"< LAVector(const ABObj< vec, ABSum, ABObj>, T>, ABObj>, T> &prod) : fSize(0), fData(0) { (*this) = prod.Obj().B(); (*this) += prod.Obj().A(); (*this) *= double(prod.f()); } // LAVector &operator+=(const LAVector &m) { // std::cout<<"LAVector& operator+=(const LAVector& m)"< LAVector &operator+=(const ABObj &m) { // std::cout<<"template LAVector& operator+=(const ABObj& m)"< LAVector &operator+=(const ABObj &m) { // std::cout<<"template LAVector& operator+=(const ABObj& m)"< LAVector &operator+=(const ABObj, ABObj>, T> &prod) { Mndspmv("U", fSize, prod.f() * prod.Obj().A().f() * prod.Obj().B().f(), prod.Obj().A().Obj().Data(), prod.Obj().B().Data(), 1, 1., fData, 1); return *this; } LAVector &operator*=(double scal) { Mndscal(fSize, scal, fData, 1); return *this; } double operator()(unsigned int i) const { assert(i < fSize); return fData[i]; } double &operator()(unsigned int i) { assert(i < fSize); return fData[i]; } double operator[](unsigned int i) const { assert(i < fSize); return fData[i]; } double &operator[](unsigned int i) { assert(i < fSize); return fData[i]; } const double *Data() const { return fData; } double *Data() { return fData; } unsigned int size() const { return fSize; } private: unsigned int fSize; double *fData; public: template LAVector &operator=(const ABObj &v) { // std::cout<<"template LAVector& operator=(ABObj& v)"< LAVector &operator=(const ABObj, T> &something) { // std::cout<<"template LAVector& operator=(const ABObj, T>& // something)"< LAVector &operator=(const ABObj, ABObj>, T> &sum) { if (fSize == 0 && fData == 0) { (*this) = sum.Obj().A(); (*this) += sum.Obj().B(); } else { LAVector tmp(sum.Obj().A()); tmp += sum.Obj().B(); assert(fSize == tmp.size()); std::memcpy(fData, tmp.Data(), fSize * sizeof(double)); } (*this) *= sum.f(); return *this; } template LAVector &operator=(const ABObj, ABObj>, T> &sum) { if (fSize == 0 && fData == 0) { (*this) = sum.Obj().B(); (*this) += sum.Obj().A(); } else { LAVector tmp(sum.Obj().A()); tmp += sum.Obj().B(); assert(fSize == tmp.size()); std::memcpy(fData, tmp.Data(), fSize * sizeof(double)); } (*this) *= sum.f(); return *this; } // template LAVector &operator=(const ABObj, ABObj>, T> &prod) { if (fSize == 0 && fData == 0) { fSize = prod.Obj().B().Obj().size(); fData = (double *)StackAllocatorHolder::Get().Allocate(sizeof(double) * fSize); Mndspmv("U", fSize, double(prod.f() * prod.Obj().A().f() * prod.Obj().B().f()), prod.Obj().A().Obj().Data(), prod.Obj().B().Obj().Data(), 1, 0., fData, 1); } else { LAVector tmp(prod.Obj().B()); assert(fSize == tmp.size()); Mndspmv("U", fSize, double(prod.f() * prod.Obj().A().f()), prod.Obj().A().Obj().Data(), tmp.Data(), 1, 0., fData, 1); } return *this; } // template LAVector & operator=(const ABObj< vec, ABSum, ABObj>, T>, ABObj>, T> &prod) { if (fSize == 0 && fData == 0) { (*this) = prod.Obj().B(); (*this) += prod.Obj().A(); } else { // std::cout<<"creating tmp variable"<, double>, double> Inverse(const ABObj &obj) { return ABObj, double>, double>( MatrixInverse, double>(obj)); } template inline ABObj, double>, double> operator*(T f, const ABObj, double>, double> &inv) { return ABObj, double>, double>(inv.Obj(), f * inv.f()); } template inline ABObj, double>, double> operator/(const ABObj, double>, double> &inv, T f) { return ABObj, double>, double>(inv.Obj(), inv.f() / f); } template inline ABObj, double>, double> operator-(const ABObj, double>, double> &inv) { return ABObj, double>, double>(inv.Obj(), T(-1.) * inv.f()); } int Invert(LASymMatrix &); int Invert_undef_sym(LASymMatrix &); /* template inline ABObj, double>, double> Inverse(const ABObj& obj) { return ABObj, double>, double>(MatrixInverse, double>(obj)); } inline ABObj, double>, double> Inverse(const ABObj& obj) { return ABObj, double>, double>(MatrixInverse, double>(obj)); } */ } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_LaInverse iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/LaOuterProduct.h0000644000000000000000000000541414332717401022206 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef MA_LaOuterProd_H_ #define MA_LaOuterProd_H_ #include "Minuit2/VectorOuterProduct.h" #include "Minuit2/ABSum.h" #include "Minuit2/LAVector.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { /// LAPACK Algebra function /// specialize the Outer_product function for LAVector; inline ABObj, double>, double> Outer_product(const ABObj &obj) { // std::cout<<"ABObj, double>, double> Outer_product(const // ABObj& obj)"<, double>, double>( VectorOuterProduct, double>(obj)); } // f*outer template inline ABObj, T>, T> operator*(T f, const ABObj, T>, T> &obj) { // std::cout<<"ABObj, T>, T> operator*(T f, const ABObj, T>, T>& obj)"<, T>, T>(obj.Obj(), obj.f() * f); } // outer/f template inline ABObj, T>, T> operator/(const ABObj, T>, T> &obj, T f) { // std::cout<<"ABObj, T>, T> operator/(const ABObj, T>, T>& obj, T f)"<, T>, T>(obj.Obj(), obj.f() / f); } // -outer template inline ABObj, T>, T> operator-(const ABObj, T>, T> &obj) { // std::cout<<"ABObj, T>, T> operator/(const ABObj, T>, T>& obj, T f)"<, T>, T>(obj.Obj(), T(-1.) * obj.f()); } void Outer_prod(LASymMatrix &, const LAVector &, double f = 1.); } // namespace Minuit2 } // namespace ROOT #endif // MA_LaOuterProd_H_ iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/LaProd.h0000644000000000000000000000473014332717401020453 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_LaProd #define ROOT_Minuit2_LaProd #include "Minuit2/ABProd.h" #include "Minuit2/LAVector.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { /* LM" remove this for fixing alpha poblem #define OP_MULT1(MT1,MT2,MAT1,MAT2,T) \ template \ inline ABObj::Type,ABProd, ABObj >,T> operator*(const ABObj& a, const ABObj& b) { return ABObj::Type,ABProd, ABObj >,T>(ABProd, ABObj >(a, b)); } \ template \ inline ABObj::Type,ABProd, ABObj >,T> operator*(const ABObj& a, const ABObj& b) { \ return ABObj::Type,ABProd, ABObj >,T>(ABProd, ABObj >(a, b)); \ } \ \ */ #define OP_MULT1(MT1, MT2, MAT1, MAT2, T) \ inline ABObj::Type, ABProd, ABObj>, T> operator*( \ const ABObj &a, const ABObj &b) \ { \ return ABObj::Type, ABProd, ABObj>, T>( \ ABProd, ABObj>(a, b)); \ } OP_MULT1(sym, vec, LASymMatrix, LAVector, double) // OP_MULT1(sym,gen,LASymMatrix,LAGenMatrix,double) // OP_MULT1(sym,sym,LASymMatrix,LASymMatrix,double) // OP_MULT1(gen,vec,LAGenMatrix,LAVector,double) // OP_MULT1(gen,sym,LAGenMatrix,LASymMatrix,double) // OP_MULT1(gen,gen,LAGenMatrix,LAGenMatrix,double) } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_LaProd iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/LaSum.h0000644000000000000000000000631614332717401020315 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_LaSum #define ROOT_Minuit2_LaSum #include "Minuit2/ABSum.h" #include "Minuit2/LAVector.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { #define OP_ADD1(MT, MAT1, T) \ inline ABObj operator-(const ABObj &m) \ { \ return ABObj(m.Obj(), T(-1.) * m.f()); \ } \ \ inline ABObj, ABObj>, T> operator+(const ABObj &a, \ const ABObj &b) \ { \ return ABObj, ABObj>, T>( \ ABSum, ABObj>(a, b)); \ } \ inline ABObj, ABObj>, T> operator-(const ABObj &a, \ const ABObj &b) \ { \ return ABObj, ABObj>, T>( \ ABSum, ABObj>(a, ABObj(b.Obj(), T(-1.) * b.f()))); \ } OP_ADD1(vec, LAVector, double) OP_ADD1(sym, LASymMatrix, double) #define OP_SCALE(MT, MAT1, T) \ inline ABObj operator*(T f, const MAT1 &obj) { return ABObj(obj, f); } OP_SCALE(sym, LASymMatrix, double) OP_SCALE(vec, LAVector, double) #define OP_SCALE1(MT, MAT1, T) \ inline ABObj operator/(const MAT1 &obj, T f) { return ABObj(obj, 1. / f); } OP_SCALE1(sym, LASymMatrix, double) OP_SCALE1(vec, LAVector, double) #define OP_MIN(MT, MAT1, T) \ inline ABObj operator-(const MAT1 &obj) { return ABObj(obj, T(-1.)); } OP_MIN(sym, LASymMatrix, double) OP_MIN(vec, LAVector, double) } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_LaSum iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MPIProcess.h0000644000000000000000000001016314332717401021253 0ustar00// @(#)root/minuit2:$Id$ // Author: A. Lazzaro 2009 /*************************************************************************** * Package: Minuit2 * * File: $Id$ * * Author: Alfio Lazzaro, alfio.lazzaro@mi.infn.it * * * * Copyright: (C) 2008 by Universita' and INFN, Milan * ***************************************************************************/ #ifndef ROOT_Minuit2_MPIProcess #define ROOT_Minuit2_MPIProcess // disable MPI calls //#define MPIPROC #include "Minuit2/MnMatrix.h" #ifdef MPIPROC #include "mpi.h" #include #endif namespace ROOT { namespace Minuit2 { class MPITerminate { public: ~MPITerminate() { #ifdef MPIPROC if (MPI::Is_initialized() && !(MPI::Is_finalized())) { std::cout << "Info --> MPITerminate:: End MPI on #" << MPI::COMM_WORLD.Get_rank() << " processor" << std::endl; MPI::Finalize(); } #endif } }; class MPIProcess { public: MPIProcess(unsigned int nelements, unsigned int indexComm); ~MPIProcess(); inline unsigned int NumElements4JobIn() const { return fNumElements4JobIn; } inline unsigned int NumElements4JobOut() const { return fNumElements4JobOut; } inline unsigned int NumElements4Job(unsigned int rank) const { return NumElements4JobIn() + ((rank < NumElements4JobOut()) ? 1 : 0); } inline unsigned int StartElementIndex() const { return ((fRank < NumElements4JobOut()) ? (fRank * NumElements4Job(fRank)) : (fNelements - (fSize - fRank) * NumElements4Job(fRank))); } inline unsigned int EndElementIndex() const { return StartElementIndex() + NumElements4Job(fRank); } inline unsigned int GetMPISize() const { return fSize; } inline unsigned int GetMPIRank() const { return fRank; } bool SyncVector(ROOT::Minuit2::MnAlgebraicVector &mnvector); bool SyncSymMatrixOffDiagonal(ROOT::Minuit2::MnAlgebraicSymMatrix &mnmatrix); static unsigned int GetMPIGlobalRank() { StartMPI(); return fgGlobalRank; } static unsigned int GetMPIGlobalSize() { StartMPI(); return fgGlobalSize; } static inline void StartMPI() { #ifdef MPIPROC if (!(MPI::Is_initialized())) { MPI::Init(); std::cout << "Info --> MPIProcess::StartMPI: Start MPI on #" << MPI::COMM_WORLD.Get_rank() << " processor" << std::endl; } fgGlobalSize = MPI::COMM_WORLD.Get_size(); fgGlobalRank = MPI::COMM_WORLD.Get_rank(); #endif } static void TerminateMPI() { #ifdef MPIPROC if (fgCommunicators[0] != 0 && fgCommunicators[1] != 0) { delete fgCommunicators[0]; fgCommunicators[0] = 0; fgIndecesComm[0] = 0; delete fgCommunicators[1]; fgCommunicators[1] = 0; fgIndecesComm[1] = 0; } MPITerminate(); #endif } static bool SetCartDimension(unsigned int dimX, unsigned int dimY); static bool SetDoFirstMPICall(bool doFirstMPICall = true); inline void SumReduce(const double &sub, double &total) { total = sub; #ifdef MPIPROC if (fSize > 1) { fgCommunicator->Allreduce(&sub, &total, 1, MPI::DOUBLE, MPI::SUM); } #endif } private: #ifdef MPIPROC void MPISyncVector(double *ivector, int svector, double *ovector); #endif private: unsigned int fNelements; unsigned int fSize; unsigned int fRank; static unsigned int fgGlobalSize; static unsigned int fgGlobalRank; static unsigned int fgCartSizeX; static unsigned int fgCartSizeY; static unsigned int fgCartDimension; static bool fgNewCart; unsigned int fNumElements4JobIn; unsigned int fNumElements4JobOut; #ifdef MPIPROC static MPI::Intracomm *fgCommunicator; static int fgIndexComm; // maximum 2 communicators, so index can be 0 and 1 static MPI::Intracomm *fgCommunicators[2]; // maximum 2 communicators static unsigned int fgIndecesComm[2]; #endif }; } // namespace Minuit2 } // namespace ROOT #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MatrixInverse.h0000644000000000000000000000263614332717401022075 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MatrixInverse #define ROOT_Minuit2_MatrixInverse #include "Minuit2/ABTypes.h" #include "Minuit2/ABObj.h" namespace ROOT { namespace Minuit2 { template class MatrixInverse { public: MatrixInverse(const M &obj) : fObject(obj) {} ~MatrixInverse() {} typedef mtype Type; const M &Obj() const { return fObject; } private: M fObject; }; template class MatrixInverse { private: MatrixInverse(const M &obj) : fObject(obj) {} public: ~MatrixInverse() {} typedef vec Type; const M &Obj() const { return fObject; } private: M fObject; }; template inline ABObj, T>, T> Inverse(const ABObj &obj) { return ABObj, T>, T>(MatrixInverse, T>(obj)); } } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MatrixInverse iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumBuilder.h0000644000000000000000000000344514332717401022216 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumBuilder #define ROOT_Minuit2_MinimumBuilder #include "Minuit2/MnTraceObject.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { class FunctionMinimum; class MnFcn; class GradientCalculator; class MinimumSeed; class MinimumState; class MnStrategy; class MinimumBuilder { public: MinimumBuilder(); virtual ~MinimumBuilder() {} virtual FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const = 0; int StorageLevel() const { return fStorageLevel; } int PrintLevel() const { return fPrintLevel; } bool TraceIter() const { return (fTracer); } MnTraceObject *TraceObject() const { return (fTracer); } virtual void SetPrintLevel(int level) { fPrintLevel = level; } virtual void SetStorageLevel(int level) { fStorageLevel = level; } // set trace object (user manages it) virtual void SetTraceObject(MnTraceObject &obj) { fTracer = &obj; } void TraceIteration(int iter, const MinimumState &state) const { if (fTracer) (*fTracer)(iter, state); } private: int fPrintLevel; int fStorageLevel; MnTraceObject *fTracer; //! tracer object (it is managed by user) }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumError.h0000644000000000000000000000562214332717401021720 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumError #define ROOT_Minuit2_MinimumError #include "Minuit2/MnConfig.h" #include "Minuit2/MnMatrix.h" #include "Minuit2/MnPrint.h" #include "Minuit2/LaSum.h" #include namespace ROOT { namespace Minuit2 { /** MinimumError keeps the inv. 2nd derivative (inv. Hessian) used for calculating the Parameter step size (-V*g) and for the covariance Update (ErrorUpdator). The covariance matrix is equal to twice the inv. Hessian. */ class MinimumError { public: enum Status { MnUnset, MnPosDef, MnMadePosDef, MnNotPosDef, MnHesseFailed, MnInvertFailed, MnReachedCallLimit, }; public: MinimumError(unsigned int n) : fPtr{new Data{{n}, 1.0, MnUnset}} {} MinimumError(const MnAlgebraicSymMatrix &mat, double dcov) : fPtr{new Data{mat, dcov, MnPosDef}} {} MinimumError(const MnAlgebraicSymMatrix &mat, Status status) : fPtr{new Data{mat, 1.0, status}} {} MnAlgebraicSymMatrix Matrix() const { return 2. * fPtr->fMatrix; } const MnAlgebraicSymMatrix &InvHessian() const { return fPtr->fMatrix; } MnAlgebraicSymMatrix Hessian() const { // calculate Heassian: inverse of error matrix MnAlgebraicSymMatrix tmp(fPtr->fMatrix); if (Invert(tmp) != 0) { MnPrint print("MinimumError::Hessian"); print.Warn("Inversion fails; return diagonal matrix"); for (unsigned int i = 0; i < fPtr->fMatrix.Nrow(); ++i) for (unsigned int j = 0; j <= i; j++) tmp(i, j) = i == j ? 1. / fPtr->fMatrix(i, i) : 0; } return tmp; } double Dcovar() const { return fPtr->fDCovar; } Status GetStatus() const { return fPtr->fStatus; } bool IsValid() const { return IsAvailable() && (IsPosDef() || IsMadePosDef()); } bool IsAccurate() const { return IsPosDef() && Dcovar() < 0.1; } bool IsPosDef() const { return GetStatus() == MnPosDef; } bool IsMadePosDef() const { return GetStatus() == MnMadePosDef; } bool HesseFailed() const { return GetStatus() == MnHesseFailed; } bool InvertFailed() const { return GetStatus() == MnInvertFailed; } bool HasReachedCallLimit() const { return GetStatus() == MnReachedCallLimit; } bool IsAvailable() const { return GetStatus() != MnUnset; } private: struct Data { MnAlgebraicSymMatrix fMatrix; double fDCovar; Status fStatus; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumError iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumErrorUpdator.h0000644000000000000000000000166714332717401023264 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumErrorUpdator #define ROOT_Minuit2_MinimumErrorUpdator namespace ROOT { namespace Minuit2 { class MinimumState; class MinimumError; class MinimumParameters; class FunctionGradient; class MinimumErrorUpdator { public: virtual ~MinimumErrorUpdator() {} virtual MinimumError Update(const MinimumState &, const MinimumParameters &, const FunctionGradient &) const = 0; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumErrorUpdator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumParameters.h0000644000000000000000000000340014332717401022722 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumParameters #define ROOT_Minuit2_MinimumParameters #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { class MinimumParameters { public: MinimumParameters(unsigned int n, double fval = 0) : fPtr{new Data{MnAlgebraicVector(n), MnAlgebraicVector(n), fval, false, false}} { } /** takes the Parameter vector */ MinimumParameters(const MnAlgebraicVector &avec, double fval) : fPtr{new Data{avec, MnAlgebraicVector(avec.size()), fval, true, false}} { } /** takes the Parameter vector plus step size x1 - x0 = dirin */ MinimumParameters(const MnAlgebraicVector &avec, const MnAlgebraicVector &dirin, double fval) : fPtr{new Data{avec, dirin, fval, true, true}} { } const MnAlgebraicVector &Vec() const { return fPtr->fParameters; } const MnAlgebraicVector &Dirin() const { return fPtr->fStepSize; } double Fval() const { return fPtr->fFVal; } bool IsValid() const { return fPtr->fValid; } bool HasStepSize() const { return fPtr->fHasStep; } private: struct Data { MnAlgebraicVector fParameters; MnAlgebraicVector fStepSize; double fFVal; bool fValid; bool fHasStep; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumParameters iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumSeed.h0000644000000000000000000000334114332717401021503 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumSeed #define ROOT_Minuit2_MinimumSeed #include "Minuit2/MinimumState.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/MinimumError.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MnUserTransformation.h" namespace ROOT { namespace Minuit2 { class MinimumSeed { public: MinimumSeed(const MinimumState &state, const MnUserTransformation &trafo) : fPtr{new Data{state, trafo, true}} {} const MinimumState &State() const { return fPtr->fState; } const MinimumParameters &Parameters() const { return State().Parameters(); } const MinimumError &Error() const { return State().Error(); }; const FunctionGradient &Gradient() const { return State().Gradient(); } const MnUserTransformation &Trafo() const { return fPtr->fTrafo; } const MnMachinePrecision &Precision() const { return Trafo().Precision(); } double Fval() const { return State().Fval(); } double Edm() const { return State().Edm(); } unsigned int NFcn() const { return State().NFcn(); } bool IsValid() const { return fPtr->fValid; } private: struct Data { MinimumState fState; MnUserTransformation fTrafo; bool fValid; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumSeed iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumSeedGenerator.h0000644000000000000000000000256114332717401023355 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumSeedGenerator #define ROOT_Minuit2_MinimumSeedGenerator namespace ROOT { namespace Minuit2 { class MinimumSeed; class MnFcn; class GradientCalculator; class MnUserParameterState; class MnStrategy; class AnalyticalGradientCalculator; /** base class for seed generators (starting values); the seed generator prepares initial starting values from the input (MnUserParameterState) for the minimization; */ class MinimumSeedGenerator { public: virtual ~MinimumSeedGenerator() {} virtual MinimumSeed operator()(const MnFcn &, const GradientCalculator &, const MnUserParameterState &, const MnStrategy &) const = 0; virtual MinimumSeed operator()(const MnFcn &, const AnalyticalGradientCalculator &, const MnUserParameterState &, const MnStrategy &) const = 0; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumSeedGenerator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinimumState.h0000644000000000000000000000624414332717401021710 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinimumState #define ROOT_Minuit2_MinimumState #include "Minuit2/MinimumParameters.h" #include "Minuit2/MinimumError.h" #include "Minuit2/FunctionGradient.h" #include namespace ROOT { namespace Minuit2 { /** MinimumState keeps the information (position, Gradient, 2nd deriv, etc) after one minimization step (usually in MinimumBuilder). */ class MinimumState { public: /// Invalid state. MinimumState(unsigned int n) : MinimumState(MinimumParameters(n, 0.0), MinimumError(n), FunctionGradient(n), 0.0, 0) { } /// Constructor without parameter values, but with function value, edm and nfcn. /// This constructor will result in a state that is flagged as not valid MinimumState(double fval, double edm, int nfcn) : MinimumState(MinimumParameters(0, fval), MinimumError(0), FunctionGradient(0), edm, nfcn) { } /// Constuctor with only parameter values, edm and nfcn, but without errors (covariance). /// The resulting state it will be considered valid, since it contains the parameter values, /// although it will has not the error matrix (MinimumError) with /// HasCovariance() returning false. MinimumState(const MinimumParameters &states, double edm, int nfcn) : MinimumState(states, MinimumError(states.Vec().size()), FunctionGradient(states.Vec().size()), edm, nfcn) { } /// Constructor with parameters values, errors and gradient MinimumState(const MinimumParameters &states, const MinimumError &err, const FunctionGradient &grad, double edm, int nfcn) : fPtr{new Data{states, err, grad, edm, nfcn}} { } const MinimumParameters &Parameters() const { return fPtr->fParameters; } const MnAlgebraicVector &Vec() const { return Parameters().Vec(); } int size() const { return Vec().size(); } const MinimumError &Error() const { return fPtr->fError; } const FunctionGradient &Gradient() const { return fPtr->fGradient; } double Fval() const { return Parameters().Fval(); } double Edm() const { return fPtr->fEDM; } int NFcn() const { return fPtr->fNFcn; } bool IsValid() const { if (HasParameters() && HasCovariance()) return Parameters().IsValid() && Error().IsValid(); else if (HasParameters()) return Parameters().IsValid(); else return false; } bool HasParameters() const { return Parameters().IsValid(); } bool HasCovariance() const { return Error().IsAvailable(); } private: struct Data { MinimumParameters fParameters; MinimumError fError; FunctionGradient fGradient; double fEDM; int fNFcn; }; std::shared_ptr fPtr; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinimumState iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinosError.h0000644000000000000000000000650014332717401021366 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinosError #define ROOT_Minuit2_MinosError #include "Minuit2/MnCross.h" #include namespace ROOT { namespace Minuit2 { //____________________________________________________________________________________ /** Class holding the result of Minos (lower and upper values) for a specific parameter */ class MinosError { public: MinosError() : fParameter(0), fMinParValue(0.), fUpper(MnCross()), fLower(MnCross()) {} MinosError(unsigned int par, double value, const MnCross &low, const MnCross &up) : fParameter(par), fMinParValue(value), fUpper(up), fLower(low) { } ~MinosError() {} MinosError(const MinosError &err) : fParameter(err.fParameter), fMinParValue(err.fMinParValue), fUpper(err.fUpper), fLower(err.fLower) { } MinosError &operator=(const MinosError &) = default; MinosError &operator()(const MinosError &err) { fParameter = err.fParameter; fMinParValue = err.fMinParValue; fUpper = err.fUpper; fLower = err.fLower; return *this; } std::pair operator()() const { return std::pair(Lower(), Upper()); } double Lower() const { if (AtLowerLimit()) return LowerState().Parameter(Parameter()).LowerLimit() - fMinParValue; if (LowerValid()) return -1. * LowerState().Error(Parameter()) * (1. + fLower.Value()); // return Hessian Error in case is invalid return -LowerState().Error(Parameter()); } double Upper() const { if (AtUpperLimit()) return UpperState().Parameter(Parameter()).UpperLimit() - fMinParValue; if (UpperValid()) return UpperState().Error(Parameter()) * (1. + fUpper.Value()); // return Hessian Error in case is invalid return UpperState().Error(Parameter()); } unsigned int Parameter() const { return fParameter; } const MnUserParameterState &LowerState() const { return fLower.State(); } const MnUserParameterState &UpperState() const { return fUpper.State(); } bool IsValid() const { return fLower.IsValid() && fUpper.IsValid(); } bool LowerValid() const { return fLower.IsValid(); } bool UpperValid() const { return fUpper.IsValid(); } bool AtLowerLimit() const { return fLower.AtLimit(); } bool AtUpperLimit() const { return fUpper.AtLimit(); } bool AtLowerMaxFcn() const { return fLower.AtMaxFcn(); } bool AtUpperMaxFcn() const { return fUpper.AtMaxFcn(); } bool LowerNewMin() const { return fLower.NewMinimum(); } bool UpperNewMin() const { return fUpper.NewMinimum(); } unsigned int NFcn() const { return fUpper.NFcn() + fLower.NFcn(); } // return parameter value at the minimum double Min() const { return fMinParValue; } private: unsigned int fParameter; double fMinParValue; MnCross fUpper; MnCross fLower; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinosError iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/Minuit2Minimizer.h0000644000000000000000000003140114332717401022500 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta Wed Oct 18 11:48:00 2006 /********************************************************************** * * * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT * * * * * **********************************************************************/ // Header file for class Minuit2Minimizer #ifndef ROOT_Minuit2_Minuit2Minimizer #define ROOT_Minuit2_Minuit2Minimizer #include "Math/Minimizer.h" #include "Minuit2/MnUserParameterState.h" #include "Math/IFunctionfwd.h" #include #include namespace ROOT { namespace Minuit2 { class ModularFunctionMinimizer; class FCNBase; class FunctionMinimum; class MnTraceObject; // enumeration specifying the type of Minuit2 minimizers enum EMinimizerType { kMigrad, kSimplex, kCombined, kScan, kFumili, kMigradBFGS }; } // namespace Minuit2 namespace Minuit2 { //_____________________________________________________________________________________________________ /** Minuit2Minimizer class implementing the ROOT::Math::Minimizer interface for Minuit2 minimization algorithm. In ROOT it can be instantiated using the plug-in manager (plug-in "Minuit2") Using a string (used by the plugin manager) or via an enumeration an one can set all the possible minimization algorithms (Migrad, Simplex, Combined, Scan and Fumili). Refer to the [guide](https://root.cern.ch/root/htmldoc/guides/minuit2/Minuit2.html) for an introduction how Minuit works. @ingroup Minuit */ class Minuit2Minimizer : public ROOT::Math::Minimizer { public: /** Default constructor */ Minuit2Minimizer(ROOT::Minuit2::EMinimizerType type = ROOT::Minuit2::kMigrad); /** Constructor with a char (used by PM) */ Minuit2Minimizer(const char *type); /** Destructor (no operations) */ ~Minuit2Minimizer() override; private: // usually copying is non trivial, so we make this unaccessible /** Copy constructor */ Minuit2Minimizer(const Minuit2Minimizer &); /** Assignment operator */ Minuit2Minimizer &operator=(const Minuit2Minimizer &rhs); public: // clear resources (parameters) for consecutives minimizations void Clear() override; /// set the function to minimize void SetFunction(const ROOT::Math::IMultiGenFunction &func) override; /// set gradient the function to minimize void SetFunction(const ROOT::Math::IMultiGradFunction &func) override; /// set free variable bool SetVariable(unsigned int ivar, const std::string &name, double val, double step) override; /// set lower limit variable (override if minimizer supports them ) bool SetLowerLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double lower) override; /// set upper limit variable (override if minimizer supports them ) bool SetUpperLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double upper) override; /// set upper/lower limited variable (override if minimizer supports them ) bool SetLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double /* lower */, double /* upper */) override; /// set fixed variable (override if minimizer supports them ) bool SetFixedVariable(unsigned int /* ivar */, const std::string & /* name */, double /* val */) override; /// set variable bool SetVariableValue(unsigned int ivar, double val) override; // set variable values bool SetVariableValues(const double *val) override; /// set the step size of an already existing variable bool SetVariableStepSize(unsigned int ivar, double step) override; /// set the lower-limit of an already existing variable bool SetVariableLowerLimit(unsigned int ivar, double lower) override; /// set the upper-limit of an already existing variable bool SetVariableUpperLimit(unsigned int ivar, double upper) override; /// set the limits of an already existing variable bool SetVariableLimits(unsigned int ivar, double lower, double upper) override; /// fix an existing variable bool FixVariable(unsigned int ivar) override; /// release an existing variable bool ReleaseVariable(unsigned int ivar) override; /// query if an existing variable is fixed (i.e. considered constant in the minimization) /// note that by default all variables are not fixed bool IsFixedVariable(unsigned int ivar) const override; /// get variable settings in a variable object (like ROOT::Fit::ParamsSettings) bool GetVariableSettings(unsigned int ivar, ROOT::Fit::ParameterSettings &varObj) const override; /// get name of variables (override if minimizer support storing of variable names) std::string VariableName(unsigned int ivar) const override; /// get index of variable given a variable given a name /// return -1 if variable is not found int VariableIndex(const std::string &name) const override; /** method to perform the minimization. Return false in case the minimization did not converge. In this case a status code different than zero is set (retrieved by the derived method Minimizer::Status() )" status = 1 : Covariance was made pos defined status = 2 : Hesse is invalid status = 3 : Edm is above max status = 4 : Reached call limit status = 5 : Any other failure */ bool Minimize() override; /// return minimum function value double MinValue() const override { return fState.Fval(); } /// return expected distance reached from the minimum double Edm() const override { return fState.Edm(); } /// return pointer to X values at the minimum const double *X() const override { return &fValues.front(); } /// return pointer to gradient values at the minimum const double *MinGradient() const override { return 0; } // not available in Minuit2 /// number of function calls to reach the minimum unsigned int NCalls() const override { return fState.NFcn(); } /// this is <= Function().NDim() which is the total /// number of variables (free+ constrained ones) unsigned int NDim() const override { return fDim; } /// number of free variables (real dimension of the problem) /// this is <= Function().NDim() which is the total unsigned int NFree() const override { return fState.VariableParameters(); } /// minimizer provides error and error matrix bool ProvidesError() const override { return true; } /// return errors at the minimum const double *Errors() const override; /** return covariance matrix elements if the variable is fixed or const the value is zero The ordering of the variables is the same as in errors and parameter value. This is different from the direct interface of Minuit2 or TMinuit where the values were obtained only to variable parameters */ double CovMatrix(unsigned int i, unsigned int j) const override; /** Fill the passed array with the covariance matrix elements if the variable is fixed or const the value is zero. The array will be filled as cov[i *ndim + j] The ordering of the variables is the same as in errors and parameter value. This is different from the direct interface of Minuit2 or TMinuit where the values were obtained only to variable parameters */ bool GetCovMatrix(double *cov) const override; /** Fill the passed array with the Hessian matrix elements The Hessian matrix is the matrix of the second derivatives and is the inverse of the covariance matrix If the variable is fixed or const the values for that variables are zero. The array will be filled as h[i *ndim + j] */ bool GetHessianMatrix(double *h) const override; /** return the status of the covariance matrix status = -1 : not available (inversion failed or Hesse failed) status = 0 : available but not positive defined status = 1 : covariance only approximate status = 2 : full matrix but forced pos def status = 3 : full accurate matrix */ int CovMatrixStatus() const override; /** return correlation coefficient between variable i and j. If the variable is fixed or const the return value is zero */ double Correlation(unsigned int i, unsigned int j) const override; /** get global correlation coefficient for the variable i. This is a number between zero and one which gives the correlation between the i-th variable and that linear combination of all other variables which is most strongly correlated with i. If the variable is fixed or const the return value is zero */ double GlobalCC(unsigned int i) const override; /** get the minos error for parameter i, return false if Minos failed A minimizaiton must be performed befre, return false if no minimization has been done In case of Minos failed the status error is updated as following status += 10 * minosStatus. The Minos status of last Minos run can also be retrieved by calling MinosStatus() */ bool GetMinosError(unsigned int i, double &errLow, double &errUp, int = 0) override; /** MINOS status code of last Minos run `status & 1 > 0` : invalid lower error `status & 2 > 0` : invalid upper error `status & 4 > 0` : invalid because maximum number of function calls exceeded `status & 8 > 0` : a new minimum has been found `status & 16 > 0` : error is truncated because parameter is at lower/upper limit */ int MinosStatus() const override { return fMinosStatus; } /** scan a parameter i around the minimum. A minimization must have been done before, return false if it is not the case */ bool Scan(unsigned int i, unsigned int &nstep, double *x, double *y, double xmin = 0, double xmax = 0) override; /** find the contour points (xi,xj) of the function for parameter i and j around the minimum The contour will be find for value of the function = Min + ErrorUp(); */ bool Contour(unsigned int i, unsigned int j, unsigned int &npoints, double *xi, double *xj) override; /** perform a full calculation of the Hessian matrix for error calculation If a valid minimum exists the calculation is done on the minimum point otherwise is performed in the current set values of parameters Status code of minimizer is updated according to the following convention (in case Hesse failed) status += 100*hesseStatus where hesse status is: status = 1 : hesse failed status = 2 : matrix inversion failed status = 3 : matrix is not pos defined */ bool Hesse() override; /// return reference to the objective function /// virtual const ROOT::Math::IGenFunction & Function() const; /// print result of minimization void PrintResults() override; /// set an object to trace operation for each iteration /// The object must be a (or inherit from) ROOT::Minuit2::MnTraceObject and implement operator() (int, const /// MinimumState & state) void SetTraceObject(MnTraceObject &obj); /// set storage level = 1 : store all iteration states (default) /// = 0 : store only first and last state to save memory void SetStorageLevel(int level); /// return the minimizer state (containing values, step size , etc..) const ROOT::Minuit2::MnUserParameterState &State() { return fState; } protected: // protected function for accessing the internal Minuit2 object. Needed for derived classes virtual const ROOT::Minuit2::ModularFunctionMinimizer *GetMinimizer() const { return fMinimizer; } virtual void SetMinimizer(ROOT::Minuit2::ModularFunctionMinimizer *m) { fMinimizer = m; } void SetMinimizerType(ROOT::Minuit2::EMinimizerType type); virtual const ROOT::Minuit2::FCNBase *GetFCN() const { return fMinuitFCN; } /// examine the minimum result bool ExamineMinimum(const ROOT::Minuit2::FunctionMinimum &min); // internal function to compute Minos errors int RunMinosError(unsigned int i, double &errLow, double &errUp, int runopt); private: unsigned int fDim; // dimension of the function to be minimized bool fUseFumili; int fMinosStatus = -1; // Minos status code ROOT::Minuit2::MnUserParameterState fState; // std::vector fMinosErrors; ROOT::Minuit2::ModularFunctionMinimizer *fMinimizer; ROOT::Minuit2::FCNBase *fMinuitFCN; ROOT::Minuit2::FunctionMinimum *fMinimum; mutable std::vector fValues; mutable std::vector fErrors; }; } // namespace Minuit2 } // end namespace ROOT #endif /* ROOT_Minuit2_Minuit2Minimizer */ iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MinuitParameter.h0000644000000000000000000001234314332717401022377 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MinuitParameter #define ROOT_Minuit2_MinuitParameter #include #include #include #include namespace ROOT { namespace Minuit2 { //____________________________________________________________________________ /** class for the individual Minuit Parameter with Name and number; contains the input numbers for the minimization or the output result from minimization; possible interactions: Fix/release, set/remove limits, set Value/error; From version 5.20: use string to store the name to avoid limitation of name length of 20 characters */ class MinuitParameter { public: // default constructor standard with value/error = 0 MinuitParameter() : fNum(0), fValue(0), fError(0.), fConst(false), fFix(false), fLoLimit(0.), fUpLimit(0.), fLoLimValid(false), fUpLimValid(false), fName("") { } // constructor for constant Parameter MinuitParameter(unsigned int num, const std::string &name, double val) : fNum(num), fValue(val), fError(0.), fConst(true), fFix(false), fLoLimit(0.), fUpLimit(0.), fLoLimValid(false), fUpLimValid(false), fName(name) { } // constructor for standard Parameter MinuitParameter(unsigned int num, const std::string &name, double val, double err) : fNum(num), fValue(val), fError(err), fConst(false), fFix(false), fLoLimit(0.), fUpLimit(0.), fLoLimValid(false), fUpLimValid(false), fName(name) { } // constructor for limited Parameter MinuitParameter(unsigned int num, const std::string &name, double val, double err, double min, double max) : fNum(num), fValue(val), fError(err), fConst(false), fFix(false), fLoLimit(min), fUpLimit(max), fLoLimValid(true), fUpLimValid(true), fName(name) { assert(min != max); if (min > max) { fLoLimit = max; fUpLimit = min; } } ~MinuitParameter() {} MinuitParameter(const MinuitParameter &par) : fNum(par.fNum), fValue(par.fValue), fError(par.fError), fConst(par.fConst), fFix(par.fFix), fLoLimit(par.fLoLimit), fUpLimit(par.fUpLimit), fLoLimValid(par.fLoLimValid), fUpLimValid(par.fUpLimValid), fName(par.fName) { } MinuitParameter &operator=(const MinuitParameter &par) { if (this != &par) { fNum = par.fNum; fName = par.fName; fValue = par.fValue; fError = par.fError; fConst = par.fConst; fFix = par.fFix; fLoLimit = par.fLoLimit; fUpLimit = par.fUpLimit; fLoLimValid = par.fLoLimValid; fUpLimValid = par.fUpLimValid; } return *this; } // access methods unsigned int Number() const { return fNum; } // new API returning a string const std::string &GetName() const { return fName; } // return const char * for mantaining backward compatibility const char *Name() const { return fName.c_str(); } double Value() const { return fValue; } double Error() const { return fError; } // interaction void SetName(const std::string &name) { fName = name; } void SetValue(double val) { fValue = val; } void SetError(double err) { fError = err; } void SetLimits(double low, double up) { assert(low != up); fLoLimit = low; fUpLimit = up; fLoLimValid = true; fUpLimValid = true; if (low > up) { fLoLimit = up; fUpLimit = low; } } void SetUpperLimit(double up) { fLoLimit = 0.; fUpLimit = up; fLoLimValid = false; fUpLimValid = true; } void SetLowerLimit(double low) { fLoLimit = low; fUpLimit = 0.; fLoLimValid = true; fUpLimValid = false; } void RemoveLimits() { fLoLimit = 0.; fUpLimit = 0.; fLoLimValid = false; fUpLimValid = false; } void Fix() { fFix = true; } void Release() { fFix = false; } // state of Parameter (fixed/const/limited) bool IsConst() const { return fConst; } bool IsFixed() const { return fFix; } bool HasLimits() const { return fLoLimValid || fUpLimValid; } bool HasLowerLimit() const { return fLoLimValid; } bool HasUpperLimit() const { return fUpLimValid; } double LowerLimit() const { return fLoLimit; } double UpperLimit() const { return fUpLimit; } private: unsigned int fNum; double fValue; double fError; bool fConst; bool fFix; double fLoLimit; double fUpLimit; bool fLoLimValid; bool fUpLimValid; std::string fName; private: // void SetName(const std::string & name) { // int l = std::min(int(strlen(name)), 11); // memset(fName, 0, 11*sizeof(char)); // memcpy(fName, name, l*sizeof(char)); // fName[10] = '\0'; // } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MinuitParameter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnApplication.h0000644000000000000000000001123714332717401022030 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnApplication #define ROOT_Minuit2_MnApplication #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnStrategy.h" #include namespace ROOT { namespace Minuit2 { class FunctionMinimum; class MinuitParameter; class MnMachinePrecision; class ModularFunctionMinimizer; class FCNBase; class FCNGradientBase; //___________________________________________________________________________ /** application interface class for minimizers (migrad, simplex, Minimize, Scan) User normally instantiates the derived class like ROOT::Minuit2::MnMigrad for using Migrad for minimization */ class MnApplication { public: /// constructor from non-gradient functions MnApplication(const FCNBase &fcn, const MnUserParameterState &state, const MnStrategy &stra, unsigned int nfcn = 0); /// constructor from gradient function MnApplication(const FCNGradientBase &fcn, const MnUserParameterState &state, const MnStrategy &stra, unsigned int nfcn = 0); virtual ~MnApplication() {} /** Minimize the function @param maxfcn : max number of function calls (if = 0) default is used which is set to 200 + 100 * npar + 5 * npar**2 @param tolerance : value used for terminating iteration procedure. For example, MIGRAD will stop iterating when edm (expected distance from minimum) will be: edm < tolerance * 10**-3 Default value of tolerance used is 0.1 */ virtual FunctionMinimum operator()(unsigned int maxfcn = 0, double tolerance = 0.1); virtual ModularFunctionMinimizer &Minimizer() = 0; virtual const ModularFunctionMinimizer &Minimizer() const = 0; const MnMachinePrecision &Precision() const { return fState.Precision(); } const MnUserParameterState &State() const { return fState; } const MnUserParameters &Parameters() const { return fState.Parameters(); } const MnUserCovariance &Covariance() const { return fState.Covariance(); } virtual const FCNBase &Fcnbase() const { return fFCN; } const MnStrategy &Strategy() const { return fStrategy; } unsigned int NumOfCalls() const { return fNumCall; } protected: const FCNBase &fFCN; MnUserParameterState fState; MnStrategy fStrategy; unsigned int fNumCall; bool fUseGrad; public: // facade: forward interface of MnUserParameters and MnUserTransformation // via MnUserParameterState // access to parameters (row-wise) const std::vector &MinuitParameters() const; // access to parameters and errors in column-wise representation std::vector Params() const; std::vector Errors() const; // access to single Parameter const MinuitParameter &Parameter(unsigned int i) const; // add free Parameter void Add(const char *Name, double val, double err); // add limited Parameter void Add(const char *Name, double val, double err, double, double); // add const Parameter void Add(const char *, double); // interaction via external number of Parameter void Fix(unsigned int); void Release(unsigned int); void SetValue(unsigned int, double); void SetError(unsigned int, double); void SetLimits(unsigned int, double, double); void RemoveLimits(unsigned int); double Value(unsigned int) const; double Error(unsigned int) const; // interaction via Name of Parameter void Fix(const char *); void Release(const char *); void SetValue(const char *, double); void SetError(const char *, double); void SetLimits(const char *, double, double); void RemoveLimits(const char *); void SetPrecision(double); double Value(const char *) const; double Error(const char *) const; // convert Name into external number of Parameter unsigned int Index(const char *) const; // convert external number into Name of Parameter const char *Name(unsigned int) const; // transformation internal <-> external double Int2ext(unsigned int, double) const; double Ext2int(unsigned int, double) const; unsigned int IntOfExt(unsigned int) const; unsigned int ExtOfInt(unsigned int) const; unsigned int VariableParameters() const; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnApplication iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnConfig.h0000644000000000000000000000124414332717401020767 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnConfig #define ROOT_Minuit2_MnConfig #ifdef _MSC_VER #pragma warning(disable : 4244) // conversion from __w64 to int #endif #if defined(__sun) && !defined(linux) #include #endif #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnContours.h0000644000000000000000000000436414332717401021404 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnContours #define ROOT_Minuit2_MnContours #include "Minuit2/MnConfig.h" #include "Minuit2/MnStrategy.h" #include #include namespace ROOT { namespace Minuit2 { class FCNBase; class FunctionMinimum; class ContoursError; //_____________________________________________________________ /** API class for Contours Error analysis (2-dim errors); minimization has to be done before and Minimum must be valid; possibility to ask only for the points or the points and associated Minos errors; */ class MnContours { public: /// construct from FCN + Minimum MnContours(const FCNBase &fcn, const FunctionMinimum &min) : fFCN(fcn), fMinimum(min), fStrategy(MnStrategy(1)) {} /// construct from FCN + Minimum + strategy MnContours(const FCNBase &fcn, const FunctionMinimum &min, unsigned int stra) : fFCN(fcn), fMinimum(min), fStrategy(MnStrategy(stra)) { } /// construct from FCN + Minimum + strategy MnContours(const FCNBase &fcn, const FunctionMinimum &min, const MnStrategy &stra) : fFCN(fcn), fMinimum(min), fStrategy(stra) { } ~MnContours() {} /// ask for one Contour (points only) from number of points (>=4) and parameter indeces std::vector> operator()(unsigned int, unsigned int, unsigned int npoints = 20) const; /// ask for one Contour ContoursError (MinosErrors + points) /// from number of points (>=4) and parameter indeces /// can be printed via std::cout ContoursError Contour(unsigned int, unsigned int, unsigned int npoints = 20) const; const MnStrategy &Strategy() const { return fStrategy; } private: const FCNBase &fFCN; const FunctionMinimum &fMinimum; MnStrategy fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnContours iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnCovarianceSqueeze.h0000644000000000000000000000227114332717401023177 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnCovarianceSqueeze #define ROOT_Minuit2_MnCovarianceSqueeze #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { class MnUserCovariance; class MinimumError; /** class to reduce the covariance matrix when a parameter is fixed by removing the corresponding row and index */ class MnCovarianceSqueeze { public: MnCovarianceSqueeze() {} ~MnCovarianceSqueeze() {} MnUserCovariance operator()(const MnUserCovariance &, unsigned int) const; MinimumError operator()(const MinimumError &, unsigned int) const; MnAlgebraicSymMatrix operator()(const MnAlgebraicSymMatrix &, unsigned int) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnCovarianceSqueeze iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnCross.h0000644000000000000000000000614214332717401020655 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnCross #define ROOT_Minuit2_MnCross #include "Minuit2/MnUserParameterState.h" namespace ROOT { namespace Minuit2 { class MnCross { public: class CrossParLimit { }; class CrossFcnLimit { }; class CrossNewMin { }; public: MnCross() : fValue(0.), fState(MnUserParameterState()), fNFcn(0), fValid(false), fLimset(false), fMaxFcn(false), fNewMin(false) { } MnCross(unsigned int nfcn) : fValue(0.), fState(MnUserParameterState()), fNFcn(nfcn), fValid(false), fLimset(false), fMaxFcn(false), fNewMin(false) { } MnCross(const MnUserParameterState &state, unsigned int nfcn) : fValue(0.), fState(state), fNFcn(nfcn), fValid(false), fLimset(false), fMaxFcn(false), fNewMin(false) { } MnCross(double value, const MnUserParameterState &state, unsigned int nfcn) : fValue(value), fState(state), fNFcn(nfcn), fValid(true), fLimset(false), fMaxFcn(false), fNewMin(false) { } MnCross(const MnUserParameterState &state, unsigned int nfcn, CrossParLimit) : fValue(0.), fState(state), fNFcn(nfcn), fValid(true), fLimset(true), fMaxFcn(false), fNewMin(false) { } MnCross(const MnUserParameterState &state, unsigned int nfcn, CrossFcnLimit) : fValue(0.), fState(state), fNFcn(nfcn), fValid(false), fLimset(false), fMaxFcn(true), fNewMin(false) { } MnCross(const MnUserParameterState &state, unsigned int nfcn, CrossNewMin) : fValue(0.), fState(state), fNFcn(nfcn), fValid(false), fLimset(false), fMaxFcn(false), fNewMin(true) { } ~MnCross() {} MnCross(const MnCross &cross) : fValue(cross.fValue), fState(cross.fState), fNFcn(cross.fNFcn), fValid(cross.fValid), fLimset(cross.fLimset), fMaxFcn(cross.fMaxFcn), fNewMin(cross.fNewMin) { } MnCross &operator=(const MnCross &) = default; MnCross &operator()(const MnCross &cross) { fValue = cross.fValue; fState = cross.fState; fNFcn = cross.fNFcn; fValid = cross.fValid; fLimset = cross.fLimset; fMaxFcn = cross.fMaxFcn; fNewMin = cross.fNewMin; return *this; } double Value() const { return fValue; } const MnUserParameterState &State() const { return fState; } bool IsValid() const { return fValid; } bool AtLimit() const { return fLimset; } bool AtMaxFcn() const { return fMaxFcn; } bool NewMinimum() const { return fNewMin; } unsigned int NFcn() const { return fNFcn; } private: double fValue; MnUserParameterState fState; unsigned int fNFcn; bool fValid; bool fLimset; bool fMaxFcn; bool fNewMin; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnCross iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnEigen.h0000644000000000000000000000166714332717401020622 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnEigen #define ROOT_Minuit2_MnEigen #include "Minuit2/MnConfig.h" #include namespace ROOT { namespace Minuit2 { class MnUserCovariance; /** API class for calculating the eigenvalues of symmetric matrix */ class MnEigen { public: MnEigen() {} ~MnEigen() {} /// calculate the eigenvalues std::vector operator()(const MnUserCovariance &) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnEigen iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnFcn.h0000644000000000000000000000333414332717401020272 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnFcn #define ROOT_Minuit2_MnFcn #include "Minuit2/MnConfig.h" #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { class FCNBase; /** Wrapper class to FCNBase interface used internally by Minuit. Apply conversion from calling the function from a Minuit Vector (MnAlgebraicVector) to a std::vector for the function coordinates. The class counts also the number of function calls. By default counter strart from zero, but a different value might be given if the class is instantiated later on, for example for a set of different minimizaitons Normally the derived class MnUserFCN should be instantiated with performs in addition the transformatiopn internal-> external parameters */ class MnFcn { public: /// constructor of explicit MnFcn(const FCNBase &fcn, int ncall = 0) : fFCN(fcn), fNumCall(ncall) {} virtual ~MnFcn(); virtual double operator()(const MnAlgebraicVector &) const; unsigned int NumOfCalls() const { return fNumCall; } // // forward interface // double ErrorDef() const; double Up() const; const FCNBase &Fcn() const { return fFCN; } private: const FCNBase &fFCN; protected: mutable int fNumCall; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnFcn iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnFumiliMinimize.h0000644000000000000000000000764614332717401022525 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnFumiliMinimize #define ROOT_Minuit2_MnFumiliMinimize #include "Minuit2/MnApplication.h" #include "Minuit2/FumiliMinimizer.h" #include "Minuit2/FumiliFCNBase.h" #include namespace ROOT { namespace Minuit2 { // class FumiliFCNBase; // class FCNBase; //___________________________________________________________________________ /** API class for minimization using Fumili technology; allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc.; also used by MnMinos and MnContours; */ class MnFumiliMinimize : public MnApplication { public: /// construct from FumiliFCNBase + std::vector for parameters and errors MnFumiliMinimize(const FumiliFCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } /// construct from FumiliFCNBase + std::vector for parameters and covariance MnFumiliMinimize(const FumiliFCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } /// construct from FumiliFCNBase + std::vector for parameters and MnUserCovariance MnFumiliMinimize(const FumiliFCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } /// construct from FumiliFCNBase + MnUserParameters MnFumiliMinimize(const FumiliFCNBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } /// construct from FumiliFCNBase + MnUserParameters + MnUserCovariance MnFumiliMinimize(const FumiliFCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } /// construct from FumiliFCNBase + MnUserParameterState + MnStrategy MnFumiliMinimize(const FumiliFCNBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(FumiliMinimizer()), fFCN(fcn) { } MnFumiliMinimize(const MnFumiliMinimize &migr) : MnApplication(migr.Fcnbase(), migr.State(), migr.Strategy(), migr.NumOfCalls()), fMinimizer(migr.fMinimizer), fFCN(migr.Fcnbase()) { } ~MnFumiliMinimize() override {} FumiliMinimizer &Minimizer() override { return fMinimizer; } const FumiliMinimizer &Minimizer() const override { return fMinimizer; } const FumiliFCNBase &Fcnbase() const override { return fFCN; } /// overwrite Minimize to use FumiliFCNBase FunctionMinimum operator()(unsigned int = 0, double = 0.1) override; private: FumiliMinimizer fMinimizer; const FumiliFCNBase &fFCN; private: // forbidden assignment of migrad (const FumiliFCNBase& = ) MnFumiliMinimize &operator=(const MnFumiliMinimize &) { return *this; } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnFumiliMinimize iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnFunctionCross.h0000644000000000000000000000247614332717401022371 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnFunctionCross #define ROOT_Minuit2_MnFunctionCross #include "Minuit2/MnConfig.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; class MnUserParameterState; class MnStrategy; class MnCross; /** MnFunctionCross */ class MnFunctionCross { public: MnFunctionCross(const FCNBase &fcn, const MnUserParameterState &state, double fval, const MnStrategy &stra) : fFCN(fcn), fState(state), fFval(fval), fStrategy(stra) { } ~MnFunctionCross() {} MnCross operator()(const std::vector &, const std::vector &, const std::vector &, double, unsigned int) const; private: const FCNBase &fFCN; const MnUserParameterState &fState; double fFval; const MnStrategy &fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnFunctionCross iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnGlobalCorrelationCoeff.h0000644000000000000000000000227214332717401024131 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnGlobalCorrelationCoeff #define ROOT_Minuit2_MnGlobalCorrelationCoeff #include "Minuit2/MnConfig.h" #include "Minuit2/MnMatrix.h" #include namespace ROOT { namespace Minuit2 { /** class for global correlation coefficient */ class MnGlobalCorrelationCoeff { public: MnGlobalCorrelationCoeff() : fGlobalCC(std::vector()), fValid(false) {} MnGlobalCorrelationCoeff(const MnAlgebraicSymMatrix &); ~MnGlobalCorrelationCoeff() {} const std::vector &GlobalCC() const { return fGlobalCC; } bool IsValid() const { return fValid; } private: std::vector fGlobalCC; bool fValid; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnGlobalCorrelationCoeff iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnHesse.h0000644000000000000000000000701114332717401020627 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnHesse #define ROOT_Minuit2_MnHesse #include "Minuit2/MnConfig.h" #include "Minuit2/MnStrategy.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; class MnUserParameterState; class MnUserParameters; class MnUserCovariance; class MnUserTransformation; class MinimumState; class MnMachinePrecision; class MnFcn; class FunctionMinimum; //_______________________________________________________________________ /** API class for calculating the numerical covariance matrix (== 2x Inverse Hessian == 2x Inverse 2nd derivative); can be used by the user or Minuit itself */ class MnHesse { public: /// default constructor with default strategy MnHesse() : fStrategy(MnStrategy(1)) {} /// constructor with user-defined strategy level MnHesse(unsigned int stra) : fStrategy(MnStrategy(stra)) {} /// conctructor with specific strategy MnHesse(const MnStrategy &stra) : fStrategy(stra) {} ~MnHesse() {} /// /// low-level API /// /// FCN + parameters + errors MnUserParameterState operator()(const FCNBase &, const std::vector &, const std::vector &, unsigned int maxcalls = 0) const; /// FCN + parameters + covariance MnUserParameterState operator()(const FCNBase &, const std::vector &, unsigned int nrow, const std::vector &, unsigned int maxcalls = 0) const; /// FCN + parameters + MnUserCovariance MnUserParameterState operator()(const FCNBase &, const std::vector &, const MnUserCovariance &, unsigned int maxcalls = 0) const; /// /// high-level API /// /// FCN + MnUserParameters MnUserParameterState operator()(const FCNBase &, const MnUserParameters &, unsigned int maxcalls = 0) const; /// FCN + MnUserParameters + MnUserCovariance MnUserParameterState operator()(const FCNBase &, const MnUserParameters &, const MnUserCovariance &, unsigned int maxcalls = 0) const; /// FCN + MnUserParameterState MnUserParameterState operator()(const FCNBase &, const MnUserParameterState &, unsigned int maxcalls = 0) const; /// /// API to use MnHesse after minimization when function mimimum is avalilable, otherwise information on the last /// state will be lost. (It would be needed to re-call the gradient and spend extra useless function calls) The /// Function Minimum is updated (modified) by adding the Hesse results as last state of minimization /// void operator()(const FCNBase &, FunctionMinimum &, unsigned int maxcalls = 0) const; /// internal interface /// MinimumState operator()(const MnFcn &, const MinimumState &, const MnUserTransformation &, unsigned int maxcalls = 0) const; /// forward interface of MnStrategy unsigned int Ncycles() const { return fStrategy.HessianNCycles(); } double Tolerstp() const { return fStrategy.HessianStepTolerance(); } double TolerG2() const { return fStrategy.HessianG2Tolerance(); } private: MnStrategy fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnHesse iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnLineSearch.h0000644000000000000000000000343014332717401021576 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnLineSearch #define ROOT_Minuit2_MnLineSearch #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { class MnFcn; class MinimumParameters; class MnMachinePrecision; class MnParabolaPoint; /** Implements a 1-dimensional minimization along a given direction (i.e. quadratic interpolation) It is independent of the algorithm that generates the direction vector. It brackets the 1-dimensional Minimum and iterates to approach the real Minimum of the n-dimensional function. @author Fred James and Matthias Winkler; comments added by Andras Zsenei and Lorenzo Moneta @ingroup Minuit */ class MnLineSearch { public: MnLineSearch() {} ~MnLineSearch() {} MnParabolaPoint operator()(const MnFcn &, const MinimumParameters &, const MnAlgebraicVector &, double, const MnMachinePrecision &) const; #ifdef USE_OTHER_LS MnParabolaPoint CubicSearch(const MnFcn &, const MinimumParameters &, const MnAlgebraicVector &, double, double, const MnMachinePrecision &) const; MnParabolaPoint BrentSearch(const MnFcn &, const MinimumParameters &, const MnAlgebraicVector &, double, double, const MnMachinePrecision &) const; #endif private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnLineSearch iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnMachinePrecision.h0000644000000000000000000000355214332717401023006 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnMachinePrecision #define ROOT_Minuit2_MnMachinePrecision #include namespace ROOT { namespace Minuit2 { /** Sets the relative floating point (double) arithmetic precision. By default the precision values are obtained from the standard functions std::numeric_limits::epsilon. The values can optionally be computed directly using the ComputePrecision() member function. For a IEEE standard floating point arithmetic the computed values and the one from std::numeric_limits::epsilon are the same. SetPrecision() method can instead be used to override Minuit's own determination, when the user knows that the {FCN} function Value is not calculated to the nominal machine accuracy. */ class MnMachinePrecision { public: MnMachinePrecision(); /// eps returns the smallest possible number so that 1.+eps > 1. double Eps() const { return fEpsMac; } /// eps2 returns 2*sqrt(eps) double Eps2() const { return fEpsMa2; } /// override Minuit's own determination void SetPrecision(double prec) { fEpsMac = prec; fEpsMa2 = 2. * std::sqrt(fEpsMac); } /// compute Machine precision directly instead /// of using default values from std::numeric_limits void ComputePrecision(); private: double fEpsMac; double fEpsMa2; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnMachinePrecision iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnMatrix.h0000644000000000000000000000167014332717401021031 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnMatrix #define ROOT_Minuit2_MnMatrix // add MnConfig file to define before everything compiler // dependent macros #include "Minuit2/MnConfig.h" #include "Minuit2/LASymMatrix.h" #include "Minuit2/LAVector.h" #include "Minuit2/LaInverse.h" #include "Minuit2/LaOuterProduct.h" namespace ROOT { namespace Minuit2 { typedef LASymMatrix MnAlgebraicSymMatrix; typedef LAVector MnAlgebraicVector; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnMatrix iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnMigrad.h0000644000000000000000000001251114332717401020764 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnMigrad #define ROOT_Minuit2_MnMigrad #include "Minuit2/MnApplication.h" #include "Minuit2/VariableMetricMinimizer.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; //_____________________________________________________________________________ /** API class for minimization using Variable Metric technology ("MIGRAD"); allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc.; also used by MnMinos and MnContours; */ class MnMigrad : public MnApplication { public: /// construct from FCNBase + std::vector for parameters and errors MnMigrad(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNBase + std::vector for parameters and covariance MnMigrad(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNBase + std::vector for parameters and MnUserCovariance MnMigrad(const FCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNBase + MnUserParameters MnMigrad(const FCNBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNBase + MnUserParameters + MnUserCovariance MnMigrad(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNBase + MnUserParameterState + MnStrategy MnMigrad(const FCNBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(VariableMetricMinimizer()) { } // constructs from gradient FCN /// construct from FCNGradientBase + std::vector for parameters and errors MnMigrad(const FCNGradientBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNGradientBase + std::vector for parameters and covariance MnMigrad(const FCNGradientBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNGradientBase + std::vector for parameters and MnUserCovariance MnMigrad(const FCNGradientBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNGradientBase + MnUserParameters MnMigrad(const FCNGradientBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNGradientBase + MnUserParameters + MnUserCovariance MnMigrad(const FCNGradientBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(VariableMetricMinimizer()) { } /// construct from FCNGradientBase + MnUserParameterState + MnStrategy MnMigrad(const FCNGradientBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(VariableMetricMinimizer()) { } ~MnMigrad() override {} /// Copy constructor, copy shares the reference to the same FCNBase in MnApplication MnMigrad(const MnMigrad &) = default; // Copy assignment deleted, since MnApplication has unassignable reference to FCNBase MnMigrad &operator=(const MnMigrad &) = delete; ModularFunctionMinimizer &Minimizer() override { return fMinimizer; } const ModularFunctionMinimizer &Minimizer() const override { return fMinimizer; } private: VariableMetricMinimizer fMinimizer; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnMigrad iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnMinimize.h0000644000000000000000000001232214332717401021342 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnMinimize #define ROOT_Minuit2_MnMinimize #include "Minuit2/MnApplication.h" #include "Minuit2/CombinedMinimizer.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; /** API class for minimization using Variable Metric technology ("MIGRAD"); allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc.; also used by MnMinos and MnContours; */ class MnMinimize : public MnApplication { public: /// construct from FCNBase + std::vector for parameters and errors MnMinimize(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNBase + std::vector for parameters and covariance MnMinimize(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNBase + std::vector for parameters and MnUserCovariance MnMinimize(const FCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNBase + MnUserParameters MnMinimize(const FCNBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNBase + MnUserParameters + MnUserCovariance MnMinimize(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNBase + MnUserParameterState + MnStrategy MnMinimize(const FCNBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(CombinedMinimizer()) { } // interfaces using FCNGradientBase /// construct from FCNGradientBase + std::vector for parameters and errors MnMinimize(const FCNGradientBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNGradientBase + std::vector for parameters and covariance MnMinimize(const FCNGradientBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNGradientBase + std::vector for parameters and MnUserCovariance MnMinimize(const FCNGradientBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNGradientBase + MnUserParameters MnMinimize(const FCNGradientBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNGradientBase + MnUserParameters + MnUserCovariance MnMinimize(const FCNGradientBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(CombinedMinimizer()) { } /// construct from FCNGradientBase + MnUserParameterState + MnStrategy MnMinimize(const FCNGradientBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(CombinedMinimizer()) { } MnMinimize(const MnMinimize &migr) : MnApplication(migr.Fcnbase(), migr.State(), migr.Strategy(), migr.NumOfCalls()), fMinimizer(migr.fMinimizer) { } ~MnMinimize() override {} ModularFunctionMinimizer &Minimizer() override { return fMinimizer; } const ModularFunctionMinimizer &Minimizer() const override { return fMinimizer; } private: CombinedMinimizer fMinimizer; private: // forbidden assignment operator MnMinimize &operator=(const MnMinimize &) { return *this; } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnMinimize iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnMinos.h0000644000000000000000000000461614332717401020655 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnMinos #define ROOT_Minuit2_MnMinos #include "Minuit2/MnStrategy.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; class FunctionMinimum; class MinosError; class MnCross; //__________________________________________________________________ /** API class for Minos Error analysis (asymmetric errors); minimization has to be done before and Minimum must be valid; possibility to ask only for one side of the Minos Error; */ class MnMinos { public: /// construct from FCN + Minimum + strategy MnMinos(const FCNBase &fcn, const FunctionMinimum &min, unsigned int stra = 1); /// construct from FCN + Minimum + strategy MnMinos(const FCNBase &fcn, const FunctionMinimum &min, const MnStrategy &stra); ~MnMinos() {} /// returns the negative (pair.first) and the positive (pair.second) /// Minos Error of the Parameter std::pair operator()(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; /// calculate one side (negative or positive Error) of the Parameter /// give as input (optionally) maxcalls and tolerance double Lower(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; double Upper(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; MnCross Loval(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; MnCross Upval(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; /// ask for MinosError (Lower + Upper) /// can be printed via std::cout MinosError Minos(unsigned int, unsigned int maxcalls = 0, double toler = 0.1) const; protected: /// internal method to get crossing value via MnFunctionCross MnCross FindCrossValue(int dir, unsigned int, unsigned int maxcalls, double toler) const; private: const FCNBase &fFCN; const FunctionMinimum &fMinimum; MnStrategy fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnMinos iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnParabola.h0000644000000000000000000000662414332717401021312 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnParabola #define ROOT_Minuit2_MnParabola #include namespace ROOT { namespace Minuit2 { /** This class defines a parabola of the form a*x*x + b*x + c @author Fred James and Matthias Winkler; comments added by Andras Zsenei and Lorenzo Moneta @ingroup Minuit */ class MnParabola { public: /** Constructor that initializes the parabola with its three parameters. @param a the coefficient of the quadratic term @param b the coefficient of the linear term @param c the constant */ MnParabola(double a, double b, double c) : fA(a), fB(b), fC(c) {} ~MnParabola() {} /** Evaluates the parabola a the point x. @param x the coordinate where the parabola needs to be evaluated. @return the y coordinate of the parabola corresponding to x. */ double Y(double x) const { return (fA * x * x + fB * x + fC); } /** Calculates the bigger of the two x values corresponding to the given y Value.

???????!!!!!!!!! And when there is none?? it looks like it will crash?? what is sqrt (-1.0) ? @param y the y Value for which the x Value is to be calculated. @return the bigger one of the two corresponding values. */ // ok, at first glance it does not look like the formula for the quadratic // equation, but it is! ;-) double X_pos(double y) const { return (std::sqrt(y / fA + Min() * Min() - fC / fA) + Min()); } // maybe it is worth to check the performance improvement with the below formula?? // double X_pos(double y) const {return (std::sqrt(y/fA + fB*fB/(4.*fA*fA) - fC/fA) - fB/(2.*fA));} /** Calculates the smaller of the two x values corresponding to the given y Value.

???????!!!!!!!!! And when there is none?? it looks like it will crash?? what is sqrt (-1.0) ? @param y the y Value for which the x Value is to be calculated. @return the smaller one of the two corresponding values. */ double X_neg(double y) const { return (-std::sqrt(y / fA + Min() * Min() - fC / fA) + Min()); } /** Calculates the x coordinate of the Minimum of the parabola. @return x coordinate of the Minimum. */ double Min() const { return -fB / (2. * fA); } /** Calculates the y coordinate of the Minimum of the parabola. @return y coordinate of the Minimum. */ double YMin() const { return (-fB * fB / (4. * fA) + fC); } /** Accessor to the coefficient of the quadratic term. @return the coefficient of the quadratic term. */ double A() const { return fA; } /** Accessor to the coefficient of the linear term. @return the coefficient of the linear term. */ double B() const { return fB; } /** Accessor to the coefficient of the constant term. @return the coefficient of the constant term. */ double C() const { return fC; } private: double fA; double fB; double fC; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnParabola iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnParabolaFactory.h0000644000000000000000000000175314332717401022640 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnParabolaFactory #define ROOT_Minuit2_MnParabolaFactory namespace ROOT { namespace Minuit2 { class MnParabola; class MnParabolaPoint; class MnParabolaFactory { public: MnParabolaFactory() {} ~MnParabolaFactory() {} MnParabola operator()(const MnParabolaPoint &, const MnParabolaPoint &, const MnParabolaPoint &) const; MnParabola operator()(const MnParabolaPoint &, double, const MnParabolaPoint &) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnParabolaFactory iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnParabolaPoint.h0000644000000000000000000000317514332717401022322 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnParabolaPoint #define ROOT_Minuit2_MnParabolaPoint namespace ROOT { namespace Minuit2 { /** A point of a parabola.

????!!!! in reality it is just a general point in two dimensional space, there is nothing that would indicate, that it belongs to a parabola. This class defines simpy an (x,y) pair!!!! @author Fred James and Matthias Winkler; comments added by Andras Zsenei and Lorenzo Moneta @ingroup Minuit \todo Should it be called MnParabolaPoint or just Point? */ class MnParabolaPoint { public: /** Initializes the point with its coordinates. @param x the x (first) coordinate of the point. @param y the y (second) coordinate of the point. */ MnParabolaPoint(double x, double y) : fX(x), fY(y) {} ~MnParabolaPoint() {} /** Accessor to the x (first) coordinate. @return the x (first) coordinate of the point. */ double X() const { return fX; } /** Accessor to the y (second) coordinate. @return the y (second) coordinate of the point. */ double Y() const { return fY; } private: double fX; double fY; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnParabolaPoint iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnParameterScan.h0000644000000000000000000000275314332717401022315 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnParameterScan #define ROOT_Minuit2_MnParameterScan #include "Minuit2/MnConfig.h" #include "Minuit2/MnUserParameters.h" #include #include namespace ROOT { namespace Minuit2 { class FCNBase; /** Scans the values of FCN as a function of one Parameter and retains the best function and Parameter values found. */ class MnParameterScan { public: MnParameterScan(const FCNBase &, const MnUserParameters &); MnParameterScan(const FCNBase &, const MnUserParameters &, double); ~MnParameterScan() {} // returns pairs of (x,y) points, x=parameter Value, y=function Value of FCN std::vector> operator()(unsigned int par, unsigned int maxsteps = 41, double low = 0., double high = 0.); const MnUserParameters &Parameters() const { return fParameters; } double Fval() const { return fAmin; } private: const FCNBase &fFCN; MnUserParameters fParameters; double fAmin; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnParameterScan iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnPlot.h0000644000000000000000000000264614332717401020507 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnPlot #define ROOT_Minuit2_MnPlot #include "Minuit2/MnConfig.h" #include #include namespace ROOT { namespace Minuit2 { /** MnPlot produces a text-screen graphical output of (x,y) points, e.g. from Scan or Contours. */ class MnPlot { public: MnPlot() : fPageWidth(80), fPageLength(30) {} MnPlot(unsigned int width, unsigned int length) : fPageWidth(width), fPageLength(length) { if (fPageWidth > 120) fPageWidth = 120; if (fPageLength > 56) fPageLength = 56; } ~MnPlot() {} void operator()(const std::vector> &) const; void operator()(double, double, const std::vector> &) const; unsigned int Width() const { return fPageWidth; } unsigned int Length() const { return fPageLength; } private: unsigned int fPageWidth; unsigned int fPageLength; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnPlot iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnPosDef.h0000644000000000000000000000202714332717401020742 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnPosDef #define ROOT_Minuit2_MnPosDef namespace ROOT { namespace Minuit2 { class MinimumState; class MinimumError; class MnMachinePrecision; /** Force the covariance matrix to be positive defined by adding extra terms in the diagonal */ class MnPosDef { public: MnPosDef() {} ~MnPosDef() {} MinimumState operator()(const MinimumState &, const MnMachinePrecision &) const; MinimumError operator()(const MinimumError &, const MnMachinePrecision &) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnPosDef iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnPrint.h0000644000000000000000000001270114332717401020656 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnPrint #define ROOT_Minuit2_MnPrint #include "Minuit2/MnConfig.h" #include #include #include #include #include namespace ROOT { namespace Minuit2 { /** define std::ostream operators for output */ class FunctionMinimum; std::ostream &operator<<(std::ostream &, const FunctionMinimum &); class MinimumState; std::ostream &operator<<(std::ostream &, const MinimumState &); class LAVector; std::ostream &operator<<(std::ostream &, const LAVector &); class LASymMatrix; std::ostream &operator<<(std::ostream &, const LASymMatrix &); class MnUserParameters; std::ostream &operator<<(std::ostream &, const MnUserParameters &); class MnUserCovariance; std::ostream &operator<<(std::ostream &, const MnUserCovariance &); class MnGlobalCorrelationCoeff; std::ostream &operator<<(std::ostream &, const MnGlobalCorrelationCoeff &); class MnUserParameterState; std::ostream &operator<<(std::ostream &, const MnUserParameterState &); class MnMachinePrecision; std::ostream &operator<<(std::ostream &, const MnMachinePrecision &); class MinosError; std::ostream &operator<<(std::ostream &, const MinosError &); class ContoursError; std::ostream &operator<<(std::ostream &, const ContoursError &); // std::pair is used by MnContour std::ostream &operator<<(std::ostream &os, const std::pair &point); /* Design notes: 1) We want to delay the costly conversion from object references to strings to a point after we have decided whether or not to show that string to the user at all. 2) We want to offer a customization point for external libraries that want to replace the MnPrint logging. The actual implementation is in a separate file, MnPrintImpl.cxx file that external libraries can replace with their own implementation. */ // logging class for messages of varying severity class MnPrint { public: // want this to be an enum class for strong typing... enum class Verbosity { Error = 0, Warn = 1, Info = 2, Debug = 3 }; // ...but also want the values accessible from MnPrint scope for convenience static constexpr auto eError = Verbosity::Error; static constexpr auto eWarn = Verbosity::Warn; static constexpr auto eInfo = Verbosity::Info; static constexpr auto eDebug = Verbosity::Debug; // used for one-line printing of fcn minimum state class Oneline { public: Oneline(double fcn, double edm, int ncalls, int iter = -1); Oneline(const MinimumState &state, int iter = -1); Oneline(const FunctionMinimum &fmin, int iter = -1); private: double fFcn, fEdm; int fNcalls, fIter; friend std::ostream &operator<<(std::ostream &os, const Oneline &x); }; MnPrint(const char *prefix, int level = MnPrint::GlobalLevel()); ~MnPrint(); // set global print level and return the previous one static int SetGlobalLevel(int level); // return current global print level static int GlobalLevel(); // Whether to show the full prefix stack or only the end static void ShowPrefixStack(bool yes); static void AddFilter(const char *prefix); static void ClearFilter(); // set print level and return the previous one int SetLevel(int level); // return current print level int Level() const; template void Error(const Ts &... args) { Log(eError, args...); } template void Warn(const Ts &... args) { Log(eWarn, args...); } template void Info(const Ts &... args) { Log(eInfo, args...); } template void Debug(const Ts &... args) { Log(eDebug, args...); } private: // low level logging template void Log(Verbosity level, const Ts &... args) { if (Level() < static_cast(level)) return; if (Hidden()) return; std::ostringstream os; StreamPrefix(os); StreamArgs(os, args...); Impl(level, os.str()); } static void StreamPrefix(std::ostringstream &os); // returns true if filters are installed and message is not selected by any filter static bool Hidden(); // see MnPrintImpl.cxx static void Impl(Verbosity level, const std::string &s); // TMP to handle lambda argument correctly, exploiting overload resolution rules template static auto HandleLambda(std::ostream &os, const T &t, int) -> decltype(t(os), void()) { t(os); } template static void HandleLambda(std::ostream &os, const T &t, float) { os << t; } static void StreamArgs(std::ostringstream &) {} // end of recursion template static void StreamArgs(std::ostringstream &os, const T &t) { os << " "; HandleLambda(os, t, 0); } template static void StreamArgs(std::ostringstream &os, const T &t, const Ts &... ts) { os << " " << t; StreamArgs(os, ts...); } int fLevel; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnPrint iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnRefCountedPointer.h0000644000000000000000000000507414332717401023166 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnRefCountedPointer #define ROOT_Minuit2_MnRefCountedPointer #include "MnReferenceCounter.h" namespace ROOT { namespace Minuit2 { template class MnRefCountedPointer { public: // Default constructor needed for use inside array, vector, etc. MnRefCountedPointer() : fPtr(0), fCounter(0) {} MnRefCountedPointer(T *pt) : fPtr(pt), fCounter(new MnReferenceCounter()) { AddReference(); } MnRefCountedPointer(const MnRefCountedPointer &other) : fPtr(other.fPtr), fCounter(other.fCounter) { AddReference(); } ~MnRefCountedPointer() { /* if(References() == 0) { if(fPtr) delete fPtr; if(fCounter) delete fCounter; } else RemoveReference(); */ if (References() != 0) RemoveReference(); } bool IsValid() const { return fPtr != 0; } MnRefCountedPointer &operator=(const MnRefCountedPointer &other) { if (this != &other && fPtr != other.fPtr) { RemoveReference(); fPtr = other.fPtr; fCounter = other.fCounter; AddReference(); } return *this; } MnRefCountedPointer &operator=(T *ptr) { if (fPtr != ptr) { fPtr = ptr; fCounter = new MnReferenceCounter(); } return *this; } T *Get() const { return fPtr; } T *operator->() const { DoCheck(); return fPtr; } T &operator*() const { DoCheck(); return *fPtr; } bool operator==(const T *otherP) const { return fPtr == otherP; } bool operator<(const T *otherP) const { return fPtr < otherP; } unsigned int References() const { return fCounter->References(); } void AddReference() const { fCounter->AddReference(); } void RemoveReference() { fCounter->RemoveReference(); if (References() == 0) { delete fPtr; fPtr = 0; delete fCounter; fCounter = 0; } } private: T *fPtr; MnReferenceCounter *fCounter; private: void DoCheck() const { assert(IsValid()); } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnRefCountedPointer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnReferenceCounter.h0000644000000000000000000000276614332717401023032 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnReferenceCounter #define ROOT_Minuit2_MnReferenceCounter #include #include "StackAllocator.h" namespace ROOT { namespace Minuit2 { // extern StackAllocator gStackAllocator; class MnReferenceCounter { public: MnReferenceCounter() : fReferences(0) {} MnReferenceCounter(const MnReferenceCounter &other) : fReferences(other.fReferences) {} MnReferenceCounter &operator=(const MnReferenceCounter &other) { fReferences = other.fReferences; return *this; } ~MnReferenceCounter() { assert(fReferences == 0); } void *operator new(size_t nbytes) { return StackAllocatorHolder::Get().Allocate(nbytes); } void operator delete(void *p, size_t /*nbytes */) { StackAllocatorHolder::Get().Deallocate(p); } unsigned int References() const { return fReferences; } void AddReference() const { fReferences++; } void RemoveReference() const { fReferences--; } private: mutable unsigned int fReferences; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnReferenceCounter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnScan.h0000644000000000000000000000657614332717401020463 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnScan #define ROOT_Minuit2_MnScan #include "Minuit2/MnApplication.h" #include "Minuit2/ScanMinimizer.h" #include #include namespace ROOT { namespace Minuit2 { class FCNBase; //_______________________________________________________________________ /** API class for minimization using a scan method to find the minimum; allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc.; */ class MnScan : public MnApplication { public: /// construct from FCNBase + std::vector for parameters and errors MnScan(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(ScanMinimizer()) { } /// construct from FCNBase + std::vector for parameters and covariance MnScan(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(ScanMinimizer()) { } /// construct from FCNBase + std::vector for parameters and MnUserCovariance MnScan(const FCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(ScanMinimizer()) { } /// construct from FCNBase + MnUserParameters MnScan(const FCNBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(ScanMinimizer()) { } /// construct from FCNBase + MnUserParameters + MnUserCovariance MnScan(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(ScanMinimizer()) { } /// construct from FCNBase + MnUserParameterState + MnStrategy MnScan(const FCNBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(ScanMinimizer()) { } MnScan(const MnScan &migr) : MnApplication(migr.Fcnbase(), migr.State(), migr.Strategy(), migr.NumOfCalls()), fMinimizer(migr.fMinimizer) { } ~MnScan() override {} ModularFunctionMinimizer &Minimizer() override { return fMinimizer; } const ModularFunctionMinimizer &Minimizer() const override { return fMinimizer; } std::vector> Scan(unsigned int par, unsigned int maxsteps = 41, double low = 0., double high = 0.); private: ScanMinimizer fMinimizer; private: /// forbidden assignment (const FCNBase& = ) MnScan &operator=(const MnScan &) { return *this; } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnScan iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnSeedGenerator.h0000644000000000000000000000236114332717401022312 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnSeedGenerator #define ROOT_Minuit2_MnSeedGenerator #include "Minuit2/MinimumSeedGenerator.h" namespace ROOT { namespace Minuit2 { /** concrete implementation of the MinimumSeedGenerator interface; used within ModularFunctionMinimizer; */ class MnSeedGenerator : public MinimumSeedGenerator { public: MnSeedGenerator() {} ~MnSeedGenerator() override {} MinimumSeed operator()(const MnFcn &, const GradientCalculator &, const MnUserParameterState &, const MnStrategy &) const override; MinimumSeed operator()(const MnFcn &, const AnalyticalGradientCalculator &, const MnUserParameterState &, const MnStrategy &) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnSeedGenerator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnSimplex.h0000644000000000000000000000703614332717401021210 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnSimplex #define ROOT_Minuit2_MnSimplex #include "Minuit2/MnApplication.h" #include "Minuit2/SimplexMinimizer.h" #include namespace ROOT { namespace Minuit2 { class FCNBase; //_________________________________________________________________________ /** API class for minimization using the Simplex method, which does not need and use the derivatives of the function, but only function values. More information on the minimization method is available here. It allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc.; */ class MnSimplex : public MnApplication { public: /// construct from FCNBase + std::vector for parameters and errors MnSimplex(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, err), MnStrategy(stra)), fMinimizer(SimplexMinimizer()) { } /// construct from FCNBase + std::vector for parameters and covariance MnSimplex(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov, nrow), MnStrategy(stra)), fMinimizer(SimplexMinimizer()) { } /// construct from FCNBase + std::vector for parameters and MnUserCovariance MnSimplex(const FCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(SimplexMinimizer()) { } /// construct from FCNBase + MnUserParameters MnSimplex(const FCNBase &fcn, const MnUserParameters &par, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par), MnStrategy(stra)), fMinimizer(SimplexMinimizer()) { } /// construct from FCNBase + MnUserParameters + MnUserCovariance MnSimplex(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int stra = 1) : MnApplication(fcn, MnUserParameterState(par, cov), MnStrategy(stra)), fMinimizer(SimplexMinimizer()) { } /// construct from FCNBase + MnUserParameterState + MnStrategy MnSimplex(const FCNBase &fcn, const MnUserParameterState &par, const MnStrategy &str) : MnApplication(fcn, MnUserParameterState(par), str), fMinimizer(SimplexMinimizer()) { } MnSimplex(const MnSimplex &migr) : MnApplication(migr.Fcnbase(), migr.State(), migr.Strategy(), migr.NumOfCalls()), fMinimizer(migr.fMinimizer) { } ~MnSimplex() override {} ModularFunctionMinimizer &Minimizer() override { return fMinimizer; } const ModularFunctionMinimizer &Minimizer() const override { return fMinimizer; } private: SimplexMinimizer fMinimizer; private: // forbidden assignment of migrad (const FCNBase& = ) MnSimplex &operator=(const MnSimplex &) { return *this; } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnSimplex iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnStrategy.h0000644000000000000000000000542614332717401021372 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnStrategy #define ROOT_Minuit2_MnStrategy namespace ROOT { namespace Minuit2 { //_________________________________________________________________________ /** API class for defining three levels of strategies: low (0), medium (1), high (>=2); acts on: Migrad (behavioural), Minos (lowers strategy by 1 for Minos-own minimization), Hesse (iterations), Numerical2PDerivative (iterations) */ class MnStrategy { public: // default strategy MnStrategy(); // user defined strategy (0, 1, >=2) explicit MnStrategy(unsigned int); ~MnStrategy() {} unsigned int Strategy() const { return fStrategy; } unsigned int GradientNCycles() const { return fGradNCyc; } double GradientStepTolerance() const { return fGradTlrStp; } double GradientTolerance() const { return fGradTlr; } unsigned int HessianNCycles() const { return fHessNCyc; } double HessianStepTolerance() const { return fHessTlrStp; } double HessianG2Tolerance() const { return fHessTlrG2; } unsigned int HessianGradientNCycles() const { return fHessGradNCyc; } int StorageLevel() const { return fStoreLevel; } bool IsLow() const { return fStrategy == 0; } bool IsMedium() const { return fStrategy == 1; } bool IsHigh() const { return fStrategy >= 2; } void SetLowStrategy(); void SetMediumStrategy(); void SetHighStrategy(); void SetGradientNCycles(unsigned int n) { fGradNCyc = n; } void SetGradientStepTolerance(double stp) { fGradTlrStp = stp; } void SetGradientTolerance(double toler) { fGradTlr = toler; } void SetHessianNCycles(unsigned int n) { fHessNCyc = n; } void SetHessianStepTolerance(double stp) { fHessTlrStp = stp; } void SetHessianG2Tolerance(double toler) { fHessTlrG2 = toler; } void SetHessianGradientNCycles(unsigned int n) { fHessGradNCyc = n; } // set storage level of iteration quantities // 0 = store only last iterations 1 = full storage (default) void SetStorageLevel(unsigned int level) { fStoreLevel = level; } private: unsigned int fStrategy; unsigned int fGradNCyc; double fGradTlrStp; double fGradTlr; unsigned int fHessNCyc; double fHessTlrStp; double fHessTlrG2; unsigned int fHessGradNCyc; int fStoreLevel; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnStrategy iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnTiny.h0000644000000000000000000000143714332717401020511 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnTiny #define ROOT_Minuit2_MnTiny namespace ROOT { namespace Minuit2 { class MnTiny { public: MnTiny() : fOne(1.) {} ~MnTiny() {} double One() const; double operator()(volatile double epsp1) const; private: double fOne; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnTiny iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnTraceObject.h0000644000000000000000000000225614332717401021753 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 2012 /********************************************************************** * * * Copyright (c) 2012 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnTraceObject #define ROOT_Minuit2_MnTraceObject namespace ROOT { namespace Minuit2 { class MinimumState; class MnUserParameterState; class MnTraceObject { public: MnTraceObject(int parNumber = -1) : fUserState(0), fParNumber(parNumber) {} virtual ~MnTraceObject() {} virtual void Init(const MnUserParameterState &state) { fUserState = &state; } virtual void operator()(int i, const MinimumState &state); const MnUserParameterState &UserState() const { return *fUserState; } void SetParNumber(int number) { fParNumber = number; } int ParNumber() const { return fParNumber; } private: const MnUserParameterState *fUserState; int fParNumber; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnTraceIter iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnUserCovariance.h0000644000000000000000000000522014332717401022471 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnUserCovariance #define ROOT_Minuit2_MnUserCovariance #include "Minuit2/MnConfig.h" #include #include namespace ROOT { namespace Minuit2 { /** Class containing the covariance matrix data represented as a vector of size n*(n+1)/2 Used to hide internal matrix representation to user */ class MnUserCovariance { public: MnUserCovariance() : fData(std::vector()), fNRow(0) {} // safe constructor using std::vector MnUserCovariance(const std::vector &data, unsigned int nrow) : fData(data), fNRow(nrow) { assert(data.size() == nrow * (nrow + 1) / 2); } // unsafe constructor using just a pointer MnUserCovariance(const double *data, unsigned int nrow) : fData(std::vector(data, data + nrow * (nrow + 1) / 2)), fNRow(nrow) { } MnUserCovariance(unsigned int n) : fData(std::vector(n * (n + 1) / 2, 0.)), fNRow(n) {} ~MnUserCovariance() {} MnUserCovariance(const MnUserCovariance &cov) : fData(cov.fData), fNRow(cov.fNRow) {} MnUserCovariance &operator=(const MnUserCovariance &cov) { if (this != &cov) { fData = cov.fData; fNRow = cov.fNRow; } return *this; } double operator()(unsigned int row, unsigned int col) const { assert(row < fNRow && col < fNRow); if (row > col) return fData[col + row * (row + 1) / 2]; else return fData[row + col * (col + 1) / 2]; } double &operator()(unsigned int row, unsigned int col) { assert(row < fNRow && col < fNRow); if (row > col) return fData[col + row * (row + 1) / 2]; else return fData[row + col * (col + 1) / 2]; } void Scale(double f) { for (unsigned int i = 0; i < fData.size(); i++) fData[i] *= f; } const std::vector &Data() const { return fData; } unsigned int Nrow() const { return fNRow; } // VC 7.1 warning: conversion from size_t to unsigned int unsigned int size() const { return static_cast(fData.size()); } private: std::vector fData; unsigned int fNRow; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnUserCovariance iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnUserFcn.h0000644000000000000000000000215114332717401021125 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnUserFcn #define ROOT_Minuit2_MnUserFcn #include "Minuit2/MnFcn.h" namespace ROOT { namespace Minuit2 { class MnUserTransformation; /** Wrapper used by Minuit of FCN interface containing a reference to the transformation object */ class MnUserFcn : public MnFcn { public: MnUserFcn(const FCNBase &fcn, const MnUserTransformation &trafo, int ncall = 0) : MnFcn(fcn, ncall), fTransform(trafo) { } ~MnUserFcn() override {} double operator()(const MnAlgebraicVector &) const override; private: const MnUserTransformation &fTransform; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnUserFcn iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnUserParameterState.h0000644000000000000000000001363014332717401023344 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnUserParameterState #define ROOT_Minuit2_MnUserParameterState #include "Minuit2/MnUserParameters.h" #include "Minuit2/MnUserCovariance.h" #include "Minuit2/MnGlobalCorrelationCoeff.h" #include #include namespace ROOT { namespace Minuit2 { class MinimumState; //_____________________________________________________________________________ /** class which holds the external user and/or internal Minuit representation of the parameters and errors; transformation internal <-> external on demand; */ class MnUserParameterState { public: /// default constructor (invalid state) MnUserParameterState() : fValid(false), fCovarianceValid(false), fGCCValid(false), fCovStatus(-1), fFVal(0), fEDM(0), fNFcn(0), fParameters(MnUserParameters()), fCovariance(MnUserCovariance()), fIntParameters(std::vector()), fIntCovariance(MnUserCovariance()) { } /// construct from user parameters (before minimization) MnUserParameterState(const std::vector &, const std::vector &); MnUserParameterState(const MnUserParameters &); /// construct from user parameters + covariance (before minimization) MnUserParameterState(const std::vector &, const std::vector &, unsigned int); MnUserParameterState(const std::vector &, const MnUserCovariance &); MnUserParameterState(const MnUserParameters &, const MnUserCovariance &); /// construct from internal parameters (after minimization) MnUserParameterState(const MinimumState &, double, const MnUserTransformation &); // user external representation const MnUserParameters &Parameters() const { return fParameters; } const MnUserCovariance &Covariance() const { return fCovariance; } const MnGlobalCorrelationCoeff &GlobalCC() const { return fGlobalCC; } // hessian (inverse of covariance matrix) MnUserCovariance Hessian() const; // Minuit internal representation const std::vector &IntParameters() const { return fIntParameters; } const MnUserCovariance &IntCovariance() const { return fIntCovariance; } // covariance matrix status (0 = not valid, 1 approximate, 2, full but made pos def, 3 accurate and not pos def int CovarianceStatus() const { return fCovStatus; } // transformation internal <-> external const MnUserTransformation &Trafo() const { return fParameters.Trafo(); } bool IsValid() const { return fValid; } bool HasCovariance() const { return fCovarianceValid; } bool HasGlobalCC() const { return fGCCValid; } double Fval() const { return fFVal; } double Edm() const { return fEDM; } unsigned int NFcn() const { return fNFcn; } public: /** facade: forward interface of MnUserParameters and MnUserTransformation */ // access to parameters (row-wise) const std::vector &MinuitParameters() const; // access to parameters and errors in column-wise representation std::vector Params() const; std::vector Errors() const; // access to single Parameter const MinuitParameter &Parameter(unsigned int i) const; // add free Parameter void Add(const std::string &name, double val, double err); // add limited Parameter void Add(const std::string &name, double val, double err, double, double); // add const Parameter void Add(const std::string &, double); // interaction via external number of Parameter void Fix(unsigned int); void Release(unsigned int); void RemoveLimits(unsigned int); void SetValue(unsigned int, double); void SetError(unsigned int, double); void SetLimits(unsigned int, double, double); void SetUpperLimit(unsigned int, double); void SetLowerLimit(unsigned int, double); void SetName(unsigned int iext, const std::string &name) { fParameters.SetName(iext, name); } double Value(unsigned int) const; double Error(unsigned int) const; // interaction via Name of Parameter void Fix(const std::string &); void Release(const std::string &); void SetValue(const std::string &, double); void SetError(const std::string &, double); void SetLimits(const std::string &, double, double); void SetUpperLimit(const std::string &, double); void SetLowerLimit(const std::string &, double); void RemoveLimits(const std::string &); double Value(const std::string &) const; double Error(const std::string &) const; // convert Name into external number of Parameter unsigned int Index(const std::string &) const; // convert external number into Name of Parameter const std::string &GetName(unsigned int) const; // mantain interface with const char * for backward compatibility const char *Name(unsigned int) const; // transformation internal <-> external double Int2ext(unsigned int, double) const; double Ext2int(unsigned int, double) const; unsigned int IntOfExt(unsigned int) const; unsigned int ExtOfInt(unsigned int) const; unsigned int VariableParameters() const; const MnMachinePrecision &Precision() const; void SetPrecision(double eps); private: bool fValid; bool fCovarianceValid; bool fGCCValid; int fCovStatus; // covariance matrix status double fFVal; double fEDM; unsigned int fNFcn; MnUserParameters fParameters; MnUserCovariance fCovariance; MnGlobalCorrelationCoeff fGlobalCC; std::vector fIntParameters; MnUserCovariance fIntCovariance; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnUserParameterState iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnUserParameters.h0000644000000000000000000001020414332717401022520 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnUserParameters #define ROOT_Minuit2_MnUserParameters #include "Minuit2/MnUserTransformation.h" #include #include namespace ROOT { namespace Minuit2 { class MnMachinePrecision; /** API class for the user interaction with the parameters; serves as input to the minimizer as well as output from it; users can interact: Fix/release parameters, set values and errors, etc.; parameters can be accessed via their Parameter number (determined internally by Minuit and followed the order how the parameters are created) or via their user-specified Name (10 character string). Minuit has also an internal parameter number which is used during the minimization (the fix parameter are skipped). The parameter number used in this class is the external one. The class ROOT::Minuit2::MnUserTransformation is used to keep the internal <-> external transformation */ class MnUserParameters { public: MnUserParameters() : fTransformation(MnUserTransformation()) {} MnUserParameters(const std::vector &, const std::vector &); ~MnUserParameters() {} MnUserParameters(const MnUserParameters &par) : fTransformation(par.fTransformation) {} MnUserParameters &operator=(const MnUserParameters &par) { fTransformation = par.fTransformation; return *this; } const MnUserTransformation &Trafo() const { return fTransformation; } unsigned int VariableParameters() const { return fTransformation.VariableParameters(); } /// access to parameters (row-wise) const std::vector &Parameters() const; /// access to parameters and errors in column-wise representation std::vector Params() const; std::vector Errors() const; /// access to single Parameter const MinuitParameter &Parameter(unsigned int) const; /// Add free Parameter Name, Value, Error bool Add(const std::string &, double, double); /// Add limited Parameter Name, Value, Lower bound, Upper bound bool Add(const std::string &, double, double, double, double); /// Add const Parameter Name, vale bool Add(const std::string &, double); /// interaction via external number of Parameter void Fix(unsigned int); void Release(unsigned int); void RemoveLimits(unsigned int); void SetValue(unsigned int, double); void SetError(unsigned int, double); void SetLimits(unsigned int, double, double); void SetUpperLimit(unsigned int, double); void SetLowerLimit(unsigned int, double); void SetName(unsigned int, const std::string &); double Value(unsigned int) const; double Error(unsigned int) const; /// interaction via Name of Parameter void Fix(const std::string &); void Release(const std::string &); void SetValue(const std::string &, double); void SetError(const std::string &, double); void SetLimits(const std::string &, double, double); void SetUpperLimit(const std::string &, double); void SetLowerLimit(const std::string &, double); void RemoveLimits(const std::string &); double Value(const std::string &) const; double Error(const std::string &) const; // convert Name into external number of Parameter unsigned int Index(const std::string &) const; // convert external number into Name of Parameter const std::string &GetName(unsigned int) const; // mantain interface with const char * for backward compatibility const char *Name(unsigned int) const; const MnMachinePrecision &Precision() const; void SetPrecision(double eps) { fTransformation.SetPrecision(eps); } private: MnUserTransformation fTransformation; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnUserParameters iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnUserTransformation.h0000644000000000000000000001433314332717401023432 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnUserTransformation #define ROOT_Minuit2_MnUserTransformation #include "Minuit2/MnConfig.h" #include "Minuit2/MnMatrix.h" #include "Minuit2/MinuitParameter.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/SinParameterTransformation.h" #include "Minuit2/SqrtLowParameterTransformation.h" #include "Minuit2/SqrtUpParameterTransformation.h" #include #include #include namespace ROOT { namespace Minuit2 { class MnUserCovariance; // class MnMachinePrecision; /** class dealing with the transformation between user specified parameters (external) and internal parameters used for minimization */ class MnUserTransformation { public: MnUserTransformation() : fPrecision(MnMachinePrecision()), fParameters(std::vector()), fExtOfInt(std::vector()), fDoubleLimTrafo(SinParameterTransformation()), fUpperLimTrafo(SqrtUpParameterTransformation()), fLowerLimTrafo(SqrtLowParameterTransformation()), fCache(std::vector()) { } MnUserTransformation(const std::vector &, const std::vector &); ~MnUserTransformation() {} MnUserTransformation(const MnUserTransformation &trafo) : fPrecision(trafo.fPrecision), fParameters(trafo.fParameters), fExtOfInt(trafo.fExtOfInt), fDoubleLimTrafo(trafo.fDoubleLimTrafo), fUpperLimTrafo(trafo.fUpperLimTrafo), fLowerLimTrafo(trafo.fLowerLimTrafo), fCache(trafo.fCache) { } MnUserTransformation &operator=(const MnUserTransformation &trafo) { if (this != &trafo) { fPrecision = trafo.fPrecision; fParameters = trafo.fParameters; fExtOfInt = trafo.fExtOfInt; fDoubleLimTrafo = trafo.fDoubleLimTrafo; fUpperLimTrafo = trafo.fUpperLimTrafo; fLowerLimTrafo = trafo.fLowerLimTrafo; fCache = trafo.fCache; } return *this; } //#ifdef MINUIT2_THREAD_SAFE // thread-safe version (do not use cache) std::vector operator()(const MnAlgebraicVector &) const; //#else // not thread safe // const std::vector& operator()(const MnAlgebraicVector&) const; //#endif // Index = internal Parameter double Int2ext(unsigned int, double) const; // Index = internal Parameter double Int2extError(unsigned int, double, double) const; MnUserCovariance Int2extCovariance(const MnAlgebraicVector &, const MnAlgebraicSymMatrix &) const; // Index = external Parameter double Ext2int(unsigned int, double) const; // Index = internal Parameter double DInt2Ext(unsigned int, double) const; // // Index = external Parameter // double dExt2Int(unsigned int, double) const; // Index = external Parameter unsigned int IntOfExt(unsigned int) const; // Index = internal Parameter unsigned int ExtOfInt(unsigned int internal) const { assert(internal < fExtOfInt.size()); return fExtOfInt[internal]; } const std::vector &Parameters() const { return fParameters; } unsigned int VariableParameters() const { return static_cast(fExtOfInt.size()); } // return initial parameter values (useful especially to get fixed parameter values) const std::vector &InitialParValues() const { return fCache; } /** forwarded interface */ const MnMachinePrecision &Precision() const { return fPrecision; } void SetPrecision(double eps) { fPrecision.SetPrecision(eps); } /// access to parameters and errors in column-wise representation std::vector Params() const; std::vector Errors() const; // access to single Parameter const MinuitParameter &Parameter(unsigned int) const; // add free Parameter bool Add(const std::string &, double, double); // add limited Parameter bool Add(const std::string &, double, double, double, double); // add const Parameter bool Add(const std::string &, double); // interaction via external number of Parameter void Fix(unsigned int); void Release(unsigned int); void RemoveLimits(unsigned int); void SetValue(unsigned int, double); void SetError(unsigned int, double); void SetLimits(unsigned int, double, double); void SetUpperLimit(unsigned int, double); void SetLowerLimit(unsigned int, double); void SetName(unsigned int, const std::string &); double Value(unsigned int) const; double Error(unsigned int) const; // interaction via Name of Parameter void Fix(const std::string &); void Release(const std::string &); void SetValue(const std::string &, double); void SetError(const std::string &, double); void SetLimits(const std::string &, double, double); void SetUpperLimit(const std::string &, double); void SetLowerLimit(const std::string &, double); void RemoveLimits(const std::string &); double Value(const std::string &) const; double Error(const std::string &) const; // convert Name into external number of Parameter (will assert if parameter is not found) unsigned int Index(const std::string &) const; // find parameter index given a name. If it is not found return a -1 int FindIndex(const std::string &) const; // convert external number into Name of Parameter (will assert if index is out of range) const std::string &GetName(unsigned int) const; // mantain interface with const char * for backward compatibility const char *Name(unsigned int) const; private: MnMachinePrecision fPrecision; std::vector fParameters; std::vector fExtOfInt; SinParameterTransformation fDoubleLimTrafo; SqrtUpParameterTransformation fUpperLimTrafo; SqrtLowParameterTransformation fLowerLimTrafo; mutable std::vector fCache; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnUserTransformation iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/MnVectorTransform.h0000644000000000000000000000205214332717401022716 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_MnVectorTransform #define ROOT_Minuit2_MnVectorTransform #include "Minuit2/MnMatrix.h" #include namespace ROOT { namespace Minuit2 { class MnVectorTransform { public: MnVectorTransform() {} ~MnVectorTransform() {} std::vector operator()(const MnAlgebraicVector &avec) const { std::vector result; result.reserve(avec.size()); for (unsigned int i = 0; i < avec.size(); i++) result.push_back(avec(i)); return result; } }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_MnVectorTransform iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ModularFunctionMinimizer.h0000644000000000000000000001121514332717401024263 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ModularFunctionMinimizer #define ROOT_Minuit2_ModularFunctionMinimizer #include "Minuit2/MnConfig.h" #include "Minuit2/FunctionMinimizer.h" #include namespace ROOT { namespace Minuit2 { class MinimumSeedGenerator; class MinimumBuilder; class MinimumSeed; class MnFcn; class GradientCalculator; class MnUserParameterState; class MnUserParameters; class MnUserCovariance; class MnStrategy; class FumiliFCNBase; //_____________________________________________________________ /** Base common class providing the API for all the minimizer Various Minimize methods are provided varying on the type of FCN function passesd and on the objects used for the parameters */ class ModularFunctionMinimizer : public FunctionMinimizer { public: ~ModularFunctionMinimizer() override {} // inherited interface FunctionMinimum Minimize(const FCNBase &, const std::vector &, const std::vector &, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override; FunctionMinimum Minimize(const FCNGradientBase &, const std::vector &, const std::vector &, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override; FunctionMinimum Minimize(const FCNBase &, const std::vector &, unsigned int, const std::vector &, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override; FunctionMinimum Minimize(const FCNGradientBase &, const std::vector &, unsigned int, const std::vector &, unsigned int stra = 1, unsigned int maxfcn = 0, double toler = 0.1) const override; // extension virtual FunctionMinimum Minimize(const FCNBase &, const MnUserParameters &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; virtual FunctionMinimum Minimize(const FCNGradientBase &, const MnUserParameters &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; virtual FunctionMinimum Minimize(const FCNBase &, const MnUserParameters &, const MnUserCovariance &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; virtual FunctionMinimum Minimize(const FCNGradientBase &, const MnUserParameters &, const MnUserCovariance &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; virtual FunctionMinimum Minimize(const FCNBase &, const MnUserParameterState &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; virtual FunctionMinimum Minimize(const FCNGradientBase &, const MnUserParameterState &, const MnStrategy &, unsigned int maxfcn = 0, double toler = 0.1) const; // for Fumili // virtual FunctionMinimum Minimize(const FumiliFCNBase&, const std::vector&, const std::vector&, // unsigned int stra=1, unsigned int maxfcn = 0, double toler = 0.1) const; // virtual FunctionMinimum Minimize(const FumiliFCNBase&, const MnUserParameters&, const MnStrategy&, unsigned int // maxfcn = 0, double toler = 0.1) const; // virtual FunctionMinimum Minimize(const FumiliFCNBase&, const MnUserParameters&, const MnUserCovariance&, const // MnStrategy&, unsigned int maxfcn = 0, double toler = 0.1) const; // virtual FunctionMinimum Minimize(const FumiliFCNBase&, const MnUserParameterState&, const MnStrategy&, unsigned // int maxfcn = 0, double toler = 0.1) const; virtual const MinimumSeedGenerator &SeedGenerator() const = 0; virtual const MinimumBuilder &Builder() const = 0; virtual MinimumBuilder &Builder() = 0; public: virtual FunctionMinimum Minimize(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ModularFunctionMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/NegativeG2LineSearch.h0000644000000000000000000000253714332717401023166 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_NegativeG2LineSearch #define ROOT_Minuit2_NegativeG2LineSearch namespace ROOT { namespace Minuit2 { class MnFcn; class MinimumState; class GradientCalculator; class MnMachinePrecision; class FunctionGradient; /** In case that one of the components of the second derivative g2 calculated by the numerical Gradient calculator is negative, a 1dim line search in the direction of that component is done in order to find a better position where g2 is again positive. */ class NegativeG2LineSearch { public: NegativeG2LineSearch() {} ~NegativeG2LineSearch() {} MinimumState operator()(const MnFcn &, const MinimumState &, const GradientCalculator &, const MnMachinePrecision &) const; bool HasNegativeG2(const FunctionGradient &, const MnMachinePrecision &) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_NegativeG2LineSearch iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/Numerical2PGradientCalculator.h0000644000000000000000000000361714332717401025106 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_Numerical2PGradientCalculator #define ROOT_Minuit2_Numerical2PGradientCalculator #include "Minuit2/MnConfig.h" #include "Minuit2/GradientCalculator.h" #include namespace ROOT { namespace Minuit2 { class MnFcn; class MnUserTransformation; class MnMachinePrecision; class MnStrategy; /** class performing the numerical gradient calculation */ class Numerical2PGradientCalculator : public GradientCalculator { public: Numerical2PGradientCalculator(const MnFcn &fcn, const MnUserTransformation &par, const MnStrategy &stra) : fFcn(fcn), fTransformation(par), fStrategy(stra) { } ~Numerical2PGradientCalculator() override {} FunctionGradient operator()(const MinimumParameters &) const override; virtual FunctionGradient operator()(const std::vector ¶ms) const; FunctionGradient operator()(const MinimumParameters &, const FunctionGradient &) const override; const MnFcn &Fcn() const { return fFcn; } const MnUserTransformation &Trafo() const { return fTransformation; } const MnMachinePrecision &Precision() const; const MnStrategy &Strategy() const { return fStrategy; } unsigned int Ncycle() const; double StepTolerance() const; double GradTolerance() const; private: const MnFcn &fFcn; const MnUserTransformation &fTransformation; const MnStrategy &fStrategy; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_Numerical2PGradientCalculator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/NumericalDerivator.h0000644000000000000000000001142014332717401023063 0ustar00// @(#)root/mathcore:$Id$ // Authors: L. Moneta, J.T. Offermann, E.G.P. Bos 2013-2018 // /********************************************************************** * * * Copyright (c) 2013 , LCG ROOT MathLib Team * * * **********************************************************************/ /* * NumericalDerivator.h * * Original version created on: Aug 14, 2013 * Authors: L. Moneta, J. T. Offermann * Modified version created on: Sep 27, 2017 * Author: E. G. P. Bos */ #ifndef ROOT_Minuit2_NumericalDerivator #define ROOT_Minuit2_NumericalDerivator #include #include #include "Fit/ParameterSettings.h" #include "Minuit2/SinParameterTransformation.h" #include "Minuit2/SqrtUpParameterTransformation.h" #include "Minuit2/SqrtLowParameterTransformation.h" #include "Minuit2/MnMachinePrecision.h" namespace ROOT { namespace Minuit2 { // Holds all necessary derivatives and associated numbers (per parameter) used in the NumericalDerivator class. struct DerivatorElement { double derivative; double second_derivative; double step_size; }; class NumericalDerivator { public: explicit NumericalDerivator(bool always_exactly_mimic_minuit2 = true); NumericalDerivator(const NumericalDerivator &other); NumericalDerivator(double step_tolerance, double grad_tolerance, unsigned int ncycles, double error_level, bool always_exactly_mimic_minuit2 = true); void SetupDifferentiate(const ROOT::Math::IBaseFunctionMultiDim *function, const double *cx, const std::vector ¶meters); std::vector Differentiate(const ROOT::Math::IBaseFunctionMultiDim *function, const double *x, const std::vector ¶meters, const std::vector &previous_gradient); DerivatorElement PartialDerivative(const ROOT::Math::IBaseFunctionMultiDim *function, const double *x, const std::vector ¶meters, unsigned int i_component, DerivatorElement previous); DerivatorElement FastPartialDerivative(const ROOT::Math::IBaseFunctionMultiDim *function, const std::vector ¶meters, unsigned int i_component, const DerivatorElement &previous); DerivatorElement operator()(const ROOT::Math::IBaseFunctionMultiDim *function, const double *x, const std::vector ¶meters, unsigned int i_component, const DerivatorElement &previous); double GetValue() const { return fVal; } inline void SetStepTolerance(double value) { fStepTolerance = value; } inline void SetGradTolerance(double value) { fGradTolerance = value; } inline void SetNCycles(unsigned int value) { fNCycles = value; } inline void SetErrorLevel(double value) { fUp = value; } double Int2ext(const ROOT::Fit::ParameterSettings ¶meter, double val) const; double Ext2int(const ROOT::Fit::ParameterSettings ¶meter, double val) const; double DInt2Ext(const ROOT::Fit::ParameterSettings ¶meter, double val) const; void SetInitialGradient(const ROOT::Math::IBaseFunctionMultiDim *function, const std::vector ¶meters, std::vector &gradient); inline bool AlwaysExactlyMimicMinuit2() const { return fAlwaysExactlyMimicMinuit2; } inline void SetAlwaysExactlyMimicMinuit2(bool flag) { fAlwaysExactlyMimicMinuit2 = flag; } private: double fStepTolerance = 0.5; double fGradTolerance = 0.1; double fUp = 1; double fVal = 0; std::vector fVx; std::vector fVxExternal; std::vector fVxFValCache; double fDfmin; double fVrysml; // MODIFIED: Minuit2 determines machine precision in a slightly different way than // std::numeric_limits::epsilon()). We go with the Minuit2 one. ROOT::Minuit2::MnMachinePrecision fPrecision; ROOT::Minuit2::SinParameterTransformation fDoubleLimTrafo; ROOT::Minuit2::SqrtUpParameterTransformation fUpperLimTrafo; ROOT::Minuit2::SqrtLowParameterTransformation fLowerLimTrafo; unsigned int fNCycles = 2; bool fAlwaysExactlyMimicMinuit2; }; std::ostream &operator<<(std::ostream &out, const DerivatorElement &value); } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_NumericalDerivator ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ParametricFunction.h����������������������������0000644�0000000�0000000�00000010513�14332717401�023063� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ParametricFunction #define ROOT_Minuit2_ParametricFunction #include "Minuit2/MnConfig.h" #include #include #include "Minuit2/FCNBase.h" namespace ROOT { namespace Minuit2 { /** Function which has parameters. For example, one could define a one-dimensional Gaussian, by considering x as an input coordinate for the evaluation of the function, and the mean and the square root of the variance as parameters.

AS OF NOW PARAMETRICFUNCTION INHERITS FROM FCNBASE INSTEAD OF GENERICFUNCTION. THIS IS ONLY BECAUSE NUMERICAL2PGRADIENTCALCULATOR NEEDS AN FCNBASE OBJECT AND WILL BE CHANGED!!!!!!!!!!!!!!!! @ingroup Minuit \todo ParametricFunction and all the classes that inherit from it are inheriting also FCNBase so that the Gradient calculation has the Up() member function. That is not really good... */ class ParametricFunction : public FCNBase { public: /** Constructor which initializes the ParametricFunction with the parameters given as input. @param params vector containing the initial Parameter values */ ParametricFunction(const std::vector ¶ms) : par(params) {} /** Constructor which initializes the ParametricFunction by setting the number of parameters. @param nparams number of parameters of the parametric function */ ParametricFunction(int nparams) : par(nparams) {} ~ParametricFunction() override {} /** Sets the parameters of the ParametricFunction. @param params vector containing the Parameter values */ virtual void SetParameters(const std::vector ¶ms) const { assert(params.size() == par.size()); par = params; } /** Accessor for the state of the parameters. @return vector containing the present Parameter settings */ virtual const std::vector &GetParameters() const { return par; } /** Accessor for the number of parameters. @return the number of function parameters */ virtual unsigned int NumberOfParameters() const { return par.size(); } // Why do I need to declare it here, it should be inherited without // any problems, no? /** Evaluates the function with the given coordinates. @param x vector containing the input coordinates @return the result of the function evaluation with the given coordinates. */ double operator()(const std::vector &x) const override = 0; /** Evaluates the function with the given coordinates and Parameter values. This member function is useful to implement when speed is an issue as it is faster to call only one function instead of two (SetParameters and operator()). The default implementation, provided for convenience, does the latter. @param x vector containing the input coordinates @param params vector containing the Parameter values @return the result of the function evaluation with the given coordinates and parameters */ virtual double operator()(const std::vector &x, const std::vector ¶ms) const { SetParameters(params); return operator()(x); } /** Member function returning the Gradient of the function with respect to its variables (but without including gradients with respect to its internal parameters). @param x vector containing the coordinates of the point where the Gradient is to be calculated. @return the Gradient vector of the function at the given point. */ virtual std::vector GetGradient(const std::vector &x) const; protected: /** The vector containing the parameters of the function It is mutable for "historical reasons" as in the hierarchy methods and classes are const and all the implications of changing them back to non-const are not clear. */ mutable std::vector par; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ParametricFunction iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ScanBuilder.h0000644000000000000000000000217514332717401021466 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ScanBuilder #define ROOT_Minuit2_ScanBuilder #include "Minuit2/MinimumBuilder.h" namespace ROOT { namespace Minuit2 { class FunctionMinimum; class MnFcn; class MinimumSeed; /** Performs a minimization using the simplex method of Nelder and Mead (ref. Comp. J. 7, 308 (1965)). */ class ScanBuilder : public MinimumBuilder { public: ScanBuilder() {} ~ScanBuilder() override {} FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ScanBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/ScanMinimizer.h0000644000000000000000000000274414332717401022045 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_ScanMinimizer #define ROOT_Minuit2_ScanMinimizer #include "Minuit2/MnConfig.h" #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/ScanBuilder.h" #include "Minuit2/SimplexSeedGenerator.h" namespace ROOT { namespace Minuit2 { //_____________________________________________________________ /** Class implementing the required methods for a minimization using SCAN API is provided in the upper ROOT::Minuit2::ModularFunctionMinimizer class */ class ScanMinimizer : public ModularFunctionMinimizer { public: ScanMinimizer() : fSeedGenerator(SimplexSeedGenerator()), fBuilder(ScanBuilder()) {} ~ScanMinimizer() override {} const MinimumSeedGenerator &SeedGenerator() const override { return fSeedGenerator; } const MinimumBuilder &Builder() const override { return fBuilder; } MinimumBuilder &Builder() override { return fBuilder; } private: SimplexSeedGenerator fSeedGenerator; ScanBuilder fBuilder; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_ScanMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SimplexBuilder.h0000644000000000000000000000221714332717401022220 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_SimplexBuilder #define ROOT_Minuit2_SimplexBuilder #include "Minuit2/MinimumBuilder.h" namespace ROOT { namespace Minuit2 { class FunctionMinimum; class MnFcn; class MinimumSeed; /** Performs a minimization using the simplex method of Nelder and Mead (ref. Comp. J. 7, 308 (1965)). */ class SimplexBuilder : public MinimumBuilder { public: SimplexBuilder() {} ~SimplexBuilder() override {} FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_SimplexBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SimplexMinimizer.h0000644000000000000000000000300314332717401022567 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_SimplexMinimizer #define ROOT_Minuit2_SimplexMinimizer #include "Minuit2/MnConfig.h" #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/SimplexBuilder.h" #include "Minuit2/SimplexSeedGenerator.h" namespace ROOT { namespace Minuit2 { //_____________________________________________________________ /** Class implementing the required methods for a minimization using Simplex. API is provided in the upper ROOT::Minuit2::ModularFunctionMinimizer class */ class SimplexMinimizer : public ModularFunctionMinimizer { public: SimplexMinimizer() : fSeedGenerator(SimplexSeedGenerator()), fBuilder(SimplexBuilder()) {} ~SimplexMinimizer() override {} const MinimumSeedGenerator &SeedGenerator() const override { return fSeedGenerator; } const MinimumBuilder &Builder() const override { return fBuilder; } MinimumBuilder &Builder() override { return fBuilder; } private: SimplexSeedGenerator fSeedGenerator; SimplexBuilder fBuilder; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_SimplexMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SimplexParameters.h0000644000000000000000000000342714332717401022741 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_SimplexParameters #define ROOT_Minuit2_SimplexParameters #include #include "Minuit2/MnMatrix.h" #include #include namespace ROOT { namespace Minuit2 { /** class describing the simplex set of points (f(x), x ) which evolve during the minimization iteration process. */ class SimplexParameters { public: SimplexParameters(const std::vector> &simpl, unsigned int jh, unsigned int jl) : fSimplexParameters(simpl), fJHigh(jh), fJLow(jl) { } ~SimplexParameters() {} void Update(double, const MnAlgebraicVector &); const std::vector> &Simplex() const { return fSimplexParameters; } const std::pair &operator()(unsigned int i) const { assert(i < fSimplexParameters.size()); return fSimplexParameters[i]; } unsigned int Jh() const { return fJHigh; } unsigned int Jl() const { return fJLow; } double Edm() const { return fSimplexParameters[Jh()].first - fSimplexParameters[Jl()].first; } MnAlgebraicVector Dirin() const; private: std::vector> fSimplexParameters; unsigned int fJHigh; unsigned int fJLow; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_SimplexParameters iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SimplexSeedGenerator.h0000644000000000000000000000243714332717401023365 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_SimplexSeedGenerator #define ROOT_Minuit2_SimplexSeedGenerator #include "Minuit2/MinimumSeedGenerator.h" namespace ROOT { namespace Minuit2 { class MinimumSeed; class MnFcn; class MnUserParameterState; class MnStrategy; /** generate Simplex starting point (state) */ class SimplexSeedGenerator : public MinimumSeedGenerator { public: SimplexSeedGenerator() {} ~SimplexSeedGenerator() override {} MinimumSeed operator()(const MnFcn &, const GradientCalculator &, const MnUserParameterState &, const MnStrategy &) const override; MinimumSeed operator()(const MnFcn &, const AnalyticalGradientCalculator &, const MnUserParameterState &, const MnStrategy &) const override; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_SimplexSeedGenerator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SinParameterTransformation.h0000644000000000000000000000243514332717401024613 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_SinParameterTransformation #define ROOT_Minuit2_SinParameterTransformation namespace ROOT { namespace Minuit2 { class MnMachinePrecision; /** class for the transformation for double-limited parameter Using a sin function one goes from a double-limited parameter range to an unlimited one */ class SinParameterTransformation { public: SinParameterTransformation() {} ~SinParameterTransformation() {} long double Int2ext(long double Value, long double Upper, long double Lower) const; long double Ext2int(long double Value, long double Upper, long double Lower, const MnMachinePrecision &) const; long double DInt2Ext(long double Value, long double Upper, long double Lower) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_SinParameterTransformation iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SqrtLowParameterTransformation.h0000644000000000000000000000303614332717401025473 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ // Project : LCG // Package : Minuit // Author : Lorenzo.MONETA@cern.ch // Created by: moneta at Thu Apr 8 10:26:22 2004 #ifndef ROOT_Minuit2_SqrtLowParameterTransformation #define ROOT_Minuit2_SqrtLowParameterTransformation namespace ROOT { namespace Minuit2 { class MnMachinePrecision; /** * Transformation from external to internal Parameter based on sqrt(1 + x**2) * * This transformation applies for the case of single side Lower Parameter limits */ class SqrtLowParameterTransformation /* : public ParameterTransformation */ { public: SqrtLowParameterTransformation() {} ~SqrtLowParameterTransformation() {} // transformation from internal to external long double Int2ext(long double Value, long double Lower) const; // transformation from external to internal long double Ext2int(long double Value, long double Lower, const MnMachinePrecision &) const; // derivative of transformation from internal to external long double DInt2Ext(long double Value, long double Lower) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/SqrtUpParameterTransformation.h0000644000000000000000000000310214332717401025310 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ // Project : LCG // Package : Minuit // Author : Lorenzo.MONETA@cern.ch // Created by: moneta at Thu Apr 8 10:26:22 2004 #ifndef ROOT_Minuit2_SqrtUpParameterTransformation #define ROOT_Minuit2_SqrtUpParameterTransformation namespace ROOT { namespace Minuit2 { class MnMachinePrecision; /** * Transformation from external to internal Parameter based on sqrt(1 + x**2) * * This transformation applies for the case of single side Upper Parameter limits */ class SqrtUpParameterTransformation /* : public ParameterTransformation */ { public: // create with user defined precision SqrtUpParameterTransformation() {} ~SqrtUpParameterTransformation() {} // transformation from internal to external long double Int2ext(long double Value, long double Upper) const; // transformation from external to internal long double Ext2int(long double Value, long double Upper, const MnMachinePrecision &) const; // derivative of transformation from internal to external long double DInt2Ext(long double Value, long double Upper) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/StackAllocator.h0000644000000000000000000001436014332717401022200 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_StackAllocator #define ROOT_Minuit2_StackAllocator #include "Minuit2/MnConfig.h" // comment out this line and recompile if you want to gain additional // performance (the gain is mainly for "simple" functions which are easy // to calculate and vanishes quickly if going to cost-intensive functions) // the library is no longer thread save however #ifdef MN_USE_STACK_ALLOC #define _MN_NO_THREAD_SAVE_ #endif #include #include namespace ROOT { namespace Minuit2 { /// define stack allocator symbol class StackOverflow { }; class StackError { }; // using namespace std; /** StackAllocator controls the memory allocation/deallocation of Minuit. If _MN_NO_THREAD_SAVE_ is defined, memory is taken from a pre-allocated piece of heap memory which is then used like a stack, otherwise via standard malloc/free. Note that defining _MN_NO_THREAD_SAVE_ makes the code thread- unsave. The gain in performance is mainly for cost-cheap FCN functions. */ class StackAllocator { public: // enum {default_size = 1048576}; enum { default_size = 524288 }; StackAllocator() : fStack(0) { #ifdef _MN_NO_THREAD_SAVE_ // std::cout<<"StackAllocator Allocate "<(fStack + offset); *ip = Value; } int ToInt(void *p) { unsigned char *pc = static_cast(p); // std::cout << "toInt: p = " << p << " fStack = " << (void*) fStack << std::endl; // VC 7.1 warning:conversin from __w64 int to int int userBlock = pc - fStack; return userBlock - sizeof(int); // correct for starting int } int AlignedSize(int nBytes) { const int fAlignment = 4; int needed = nBytes % fAlignment == 0 ? nBytes : (nBytes / fAlignment + 1) * fAlignment; return needed + 2 * sizeof(int); } void CheckOverflow(int n) { if (fStackOffset + n >= default_size) { // std::cout << " no more space on stack allocator" << std::endl; throw StackOverflow(); } } bool CheckConsistency() { // std::cout << "checking consistency for " << fBlockCount << " blocks"<< std::endl; // loop over all blocks int beg = 0; int end = fStackOffset; int nblocks = 0; while (beg < fStackOffset) { end = ReadInt(beg); // std::cout << "beg = " << beg << " end = " << end // << " fStackOffset = " << fStackOffset << std::endl; int beg2 = ReadInt(end - sizeof(int)); if (beg != beg2) { // std::cout << " beg != beg2 " << std::endl; return false; } nblocks++; beg = end; } if (end != fStackOffset) { // std::cout << " end != fStackOffset" << std::endl; return false; } if (nblocks != fBlockCount) { // std::cout << "nblocks != fBlockCount" << std::endl; return false; } // std::cout << "Allocator is in consistent state, nblocks = " << nblocks << std::endl; return true; } private: unsigned char *fStack; // unsigned char fStack[default_size]; int fStackOffset; int fBlockCount; }; class StackAllocatorHolder { // t.b.d need to use same trick as Boost singleton.hpp to be sure that // StackAllocator is created before main() public: static StackAllocator &Get() { static StackAllocator gStackAllocator; return gStackAllocator; } }; } // namespace Minuit2 } // namespace ROOT #endif iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/VariableMetricBuilder.h0000644000000000000000000000467314332717401023500 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_VariableMetricBuilder #define ROOT_Minuit2_VariableMetricBuilder #include "Minuit2/MnConfig.h" #include "Minuit2/MinimumBuilder.h" #include "Minuit2/VariableMetricEDMEstimator.h" #include "Minuit2/DavidonErrorUpdator.h" #include "Minuit2/BFGSErrorUpdator.h" #include #include namespace ROOT { namespace Minuit2 { /** Build (find) function minimum using the Variable Metric method (MIGRAD) Two possible error updators can be choosen - Davidon : this is the standard formula used in Migrad - BFGS this is the new formula based on BFGS algorithm (see Broyden–Fletcher–Goldfarb–Shanno algorithm https://en.wikipedia.org/wiki/Broyden–Fletcher–Goldfarb–Shanno_algorithm ) */ class VariableMetricBuilder : public MinimumBuilder { public: enum ErrorUpdatorType { kDavidon, kBFGS }; VariableMetricBuilder(ErrorUpdatorType type = kDavidon) : fEstimator(VariableMetricEDMEstimator()) { if (type == kBFGS) fErrorUpdator = std::unique_ptr(new BFGSErrorUpdator()); else fErrorUpdator = std::unique_ptr(new DavidonErrorUpdator()); } ~VariableMetricBuilder() override {} FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, const MnStrategy &, unsigned int, double) const override; FunctionMinimum Minimum(const MnFcn &, const GradientCalculator &, const MinimumSeed &, std::vector &, unsigned int, double) const; const VariableMetricEDMEstimator &Estimator() const { return fEstimator; } const MinimumErrorUpdator &ErrorUpdator() const { return *fErrorUpdator; } void AddResult(std::vector &result, const MinimumState &state) const; private: VariableMetricEDMEstimator fEstimator; std::shared_ptr fErrorUpdator; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_VariableMetricBuilder iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/VariableMetricEDMEstimator.h0000644000000000000000000000165014332717401024377 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_VariableMetricEDMEstimator #define ROOT_Minuit2_VariableMetricEDMEstimator namespace ROOT { namespace Minuit2 { class FunctionGradient; class MinimumError; class VariableMetricEDMEstimator { public: VariableMetricEDMEstimator() {} ~VariableMetricEDMEstimator() {} double Estimate(const FunctionGradient &, const MinimumError &) const; private: }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_VariableMetricEDMEstimator iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/VariableMetricMinimizer.h0000644000000000000000000000346114332717401024047 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_VariableMetricMinimizer #define ROOT_Minuit2_VariableMetricMinimizer #include "Minuit2/MnConfig.h" #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/MnSeedGenerator.h" #include "Minuit2/VariableMetricBuilder.h" namespace ROOT { namespace Minuit2 { class BFGSMinimizerType { }; //______________________________________________________________________________ /** Instantiates the SeedGenerator and MinimumBuilder for Variable Metric Minimization method. API is provided in the upper ROOT::Minuit2::ModularFunctionMinimizer class */ class VariableMetricMinimizer : public ModularFunctionMinimizer { public: class BFGSType { }; VariableMetricMinimizer() : fMinSeedGen(MnSeedGenerator()), fMinBuilder(VariableMetricBuilder()) {} VariableMetricMinimizer(BFGSType) : fMinSeedGen(MnSeedGenerator()), fMinBuilder(VariableMetricBuilder(VariableMetricBuilder::kBFGS)) { } ~VariableMetricMinimizer() override {} const MinimumSeedGenerator &SeedGenerator() const override { return fMinSeedGen; } const MinimumBuilder &Builder() const override { return fMinBuilder; } MinimumBuilder &Builder() override { return fMinBuilder; } private: MnSeedGenerator fMinSeedGen; VariableMetricBuilder fMinBuilder; }; } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_VariableMetricMinimizer iminuit-2.24.0/extern/root/math/minuit2/inc/Minuit2/VectorOuterProduct.h0000644000000000000000000000227314332717401023114 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_Minuit2_VectorOuterProduct #define ROOT_Minuit2_VectorOuterProduct #include "Minuit2/ABTypes.h" #include "Minuit2/ABObj.h" namespace ROOT { namespace Minuit2 { template class VectorOuterProduct { public: VectorOuterProduct(const M &obj) : fObject(obj) {} ~VectorOuterProduct() {} typedef sym Type; const M &Obj() const { return fObject; } private: M fObject; }; template inline ABObj, T>, T> Outer_product(const ABObj &obj) { return ABObj, T>, T>(VectorOuterProduct, T>(obj)); } } // namespace Minuit2 } // namespace ROOT #endif // ROOT_Minuit2_VectorOuterProduct iminuit-2.24.0/extern/root/math/minuit2/inc/TMinuit2TraceObject.h0000644000000000000000000000300714332717401021520 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 2012 /********************************************************************** * * * Copyright (c) 2012 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #ifndef ROOT_TMinuit2TraceObject #define ROOT_TMinuit2TraceObject #include "TNamed.h" #include "Minuit2/MnTraceObject.h" class TH1; class TVirtualPad; class TList; namespace ROOT { namespace Minuit2 { class MinimumState; class MnUserParameterState; } // namespace Minuit2 } // namespace ROOT class TMinuit2TraceObject : public ROOT::Minuit2::MnTraceObject, public TNamed { public: TMinuit2TraceObject(int parNumber = -1); ~TMinuit2TraceObject() override; void Init(const ROOT::Minuit2::MnUserParameterState &state) override; void operator()(int i, const ROOT::Minuit2::MinimumState &state) override; ClassDefOverride(TMinuit2TraceObject, 0) // Example Trace Object for Minuit2 private : int fIterOffset; // offset in iteration in case of combined minimizers TH1 *fHistoFval; // Function value histogram TH1 *fHistoEdm; // Edm histogram TList *fHistoParList; // list of parameter values histograms TVirtualPad *fOldPad; // old existing current pad TVirtualPad *fMinuitPad; // new pad with trace histograms }; #endif // ROOT_TMinuit2TraceObject iminuit-2.24.0/extern/root/math/minuit2/src/AnalyticalGradientCalculator.cxx0000644000000000000000000000444514332717401024110 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/AnalyticalGradientCalculator.h" #include "Minuit2/FCNGradientBase.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/MnMatrix.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { FunctionGradient AnalyticalGradientCalculator::operator()(const MinimumParameters &par) const { // evaluate analytical gradient. take care of parameter transformations std::vector grad = fGradCalc.Gradient(fTransformation(par.Vec())); assert(grad.size() == fTransformation.Parameters().size()); MnAlgebraicVector v(par.Vec().size()); for (unsigned int i = 0; i < par.Vec().size(); i++) { unsigned int ext = fTransformation.ExtOfInt(i); if (fTransformation.Parameter(ext).HasLimits()) { // double dd = (fTransformation.Parameter(ext).Upper() - // fTransformation.Parameter(ext).Lower())*0.5*cos(par.Vec()(i)); // const ParameterTransformation * pt = fTransformation.transformation(ext); // double dd = pt->dInt2ext(par.Vec()(i), fTransformation.Parameter(ext).Lower(), // fTransformation.Parameter(ext).Upper() ); double dd = fTransformation.DInt2Ext(i, par.Vec()(i)); v(i) = dd * grad[ext]; } else { v(i) = grad[ext]; } } MnPrint print("AnalyticalGradientCalculator"); print.Debug("User given gradient in Minuit2", v); return FunctionGradient(v); } FunctionGradient AnalyticalGradientCalculator::operator()(const MinimumParameters &par, const FunctionGradient &) const { // needed from base class return (*this)(par); } bool AnalyticalGradientCalculator::CheckGradient() const { // check to be sure FCN implements analytical gradient return fGradCalc.CheckGradient(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/BFGSErrorUpdator.cxx0000644000000000000000000001055414332717401021427 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/BFGSErrorUpdator.h" #include "Minuit2/MinimumState.h" #include "Minuit2/LaSum.h" #include "Minuit2/LaProd.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { double inner_product(const LAVector &, const LAVector &); double similarity(const LAVector &, const LASymMatrix &); double sum_of_elements(const LASymMatrix &); // define here a square matrix that it is needed for computingthe BFGS update // define just the class, no need for defining operatipons as dane for the Symmetric matrices // since the square matrix will be converted in a symmetric one afterwards class LASquareMatrix { public: LASquareMatrix(unsigned int n) : fNRow(n), fData(std::vector(n * n)) {} double operator()(unsigned int row, unsigned int col) const { assert(row < fNRow && col < fNRow); return fData[col + row * fNRow]; } double &operator()(unsigned int row, unsigned int col) { assert(row < fNRow && col < fNRow); return fData[col + row * fNRow]; } unsigned int Nrow() const { return fNRow; } private: unsigned int fNRow; std::vector fData; }; // compute outer product of two vector of same size to return a square matrix LASquareMatrix OuterProduct(const LAVector &v1, const LAVector &v2) { assert(v1.size() == v2.size()); LASquareMatrix a(v1.size()); for (unsigned int i = 0; i < v1.size(); ++i) { for (unsigned int j = 0; j < v2.size(); ++j) { a(i, j) = v1[i] * v2[j]; } } return a; } // compute product of symmetric matrix with square matrix LASquareMatrix MatrixProduct(const LASymMatrix &m1, const LASquareMatrix &m2) { unsigned int n = m1.Nrow(); assert(n == m2.Nrow()); LASquareMatrix a(n); for (unsigned int i = 0; i < n; ++i) { for (unsigned int j = 0; j < n; ++j) { a(i, j) = 0; for (unsigned int k = 0; k < n; ++k) { a(i, j) += m1(i, k) * m2(k, j); } } } return a; } MinimumError BFGSErrorUpdator::Update(const MinimumState &s0, const MinimumParameters &p1, const FunctionGradient &g1) const { // update of the covarianze matrix (BFGS formula, see Tutorial, par. 4.8 pag 26) // in case of delgam > gvg (PHI > 1) use rank one formula // see par 4.10 pag 30 const MnAlgebraicSymMatrix &v0 = s0.Error().InvHessian(); MnAlgebraicVector dx = p1.Vec() - s0.Vec(); MnAlgebraicVector dg = g1.Vec() - s0.Gradient().Vec(); double delgam = inner_product(dx, dg); // this is s^T y using wikipedia conventions double gvg = similarity(dg, v0); // this is y^T B^-1 y MnPrint print("BFGSErrorUpdator"); print.Debug("dx", dx, "dg", dg, "delgam", delgam, "gvg", gvg); if (delgam == 0) { print.Warn("delgam = 0 : cannot update - return same matrix"); return s0.Error(); } if (delgam < 0) { print.Warn("delgam < 0 : first derivatives increasing along search line"); } if (gvg <= 0) { // since v0 is pos def this gvg can be only = 0 if dg = 0 - should never be here print.Warn("gvg <= 0"); // return s0.Error(); } // compute update formula for BFGS // see wikipedia https://en.wikipedia.org/wiki/Broyden–Fletcher–Goldfarb–Shanno_algorithm // need to compute outer product dg . dx and it is not symmetric anymore LASquareMatrix a = OuterProduct(dg, dx); LASquareMatrix b = MatrixProduct(v0, a); unsigned int n = v0.Nrow(); MnAlgebraicSymMatrix v2(n); for (unsigned int i = 0; i < n; ++i) { for (unsigned int j = i; j < n; ++j) { v2(i, j) = (b(i, j) + b(j, i)) / (delgam); } } MnAlgebraicSymMatrix vUpd = (delgam + gvg) * Outer_product(dx) / (delgam * delgam); vUpd -= v2; double sum_upd = sum_of_elements(vUpd); vUpd += v0; double dcov = 0.5 * (s0.Error().Dcovar() + sum_upd / sum_of_elements(vUpd)); print.Debug("dcov", dcov); return MinimumError(vUpd, dcov); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/BasicMinimumError.cxx0000644000000000000000000000217114332717401021720 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/BasicMinimumError.h" #include "Minuit2/MnMatrix.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { MnAlgebraicSymMatrix BasicMinimumError::Hessian() const { // calculate Heassian: inverse of error matrix MnAlgebraicSymMatrix tmp(fMatrix); int ifail = Invert(tmp); if (ifail != 0) { MnPrint print("BasicMinimumError::Hessian"); print.Warn("Inversion fails; return diagonal matrix"); MnAlgebraicSymMatrix tmp2(fMatrix.Nrow()); for (unsigned int i = 0; i < fMatrix.Nrow(); i++) { tmp2(i, i) = 1. / fMatrix(i, i); } return tmp2; } return tmp; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/CMakeLists.txt0000644000000000000000000001215614332717401020351 0ustar00# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. # All rights reserved. # # For the licensing terms see $ROOTSYS/LICENSE. # For the list of contributors see $ROOTSYS/README/CREDITS. add_subdirectory(math) set(MINUIT2_HEADERS ABObj.h ABProd.h ABSum.h ABTypes.h AnalyticalGradientCalculator.h BFGSErrorUpdator.h CombinedMinimizer.h CombinedMinimumBuilder.h ContoursError.h DavidonErrorUpdator.h ExternalInternalGradientCalculator.h FCNAdapter.h FCNBase.h FCNGradAdapter.h FCNGradientBase.h FumiliBuilder.h FumiliChi2FCN.h FumiliErrorUpdator.h FumiliFCNAdapter.h FumiliFCNBase.h FumiliGradientCalculator.h FumiliMaximumLikelihoodFCN.h FumiliMinimizer.h FumiliStandardChi2FCN.h FumiliStandardMaximumLikelihoodFCN.h FunctionGradient.h FunctionMinimizer.h FunctionMinimum.h GenericFunction.h GradientCalculator.h HessianGradientCalculator.h InitialGradientCalculator.h LASymMatrix.h LAVector.h LaInverse.h LaOuterProduct.h LaProd.h LaSum.h MPIProcess.h MatrixInverse.h MinimumBuilder.h MinimumError.h MinimumErrorUpdator.h MinimumParameters.h MinimumSeed.h MinimumSeedGenerator.h MinimumState.h MinosError.h Minuit2Minimizer.h MinuitParameter.h MnApplication.h MnConfig.h MnContours.h MnCovarianceSqueeze.h MnCross.h MnEigen.h MnFcn.h MnFumiliMinimize.h MnFunctionCross.h MnGlobalCorrelationCoeff.h MnHesse.h MnLineSearch.h MnMachinePrecision.h MnMatrix.h MnMigrad.h MnMinimize.h MnMinos.h MnParabola.h MnParabolaFactory.h MnParabolaPoint.h MnParameterScan.h MnPlot.h MnPosDef.h MnPrint.h MnScan.h MnSeedGenerator.h MnSimplex.h MnStrategy.h MnTiny.h MnTraceObject.h MnUserCovariance.h MnUserFcn.h MnUserParameterState.h MnUserParameters.h MnUserTransformation.h MnVectorTransform.h ModularFunctionMinimizer.h NegativeG2LineSearch.h Numerical2PGradientCalculator.h ParametricFunction.h ScanBuilder.h ScanMinimizer.h SimplexBuilder.h SimplexMinimizer.h SimplexParameters.h SimplexSeedGenerator.h SinParameterTransformation.h SqrtLowParameterTransformation.h SqrtUpParameterTransformation.h StackAllocator.h VariableMetricBuilder.h VariableMetricEDMEstimator.h VariableMetricMinimizer.h VectorOuterProduct.h ) set(MINUIT2_SOURCES AnalyticalGradientCalculator.cxx BFGSErrorUpdator.cxx CombinedMinimumBuilder.cxx DavidonErrorUpdator.cxx ExternalInternalGradientCalculator.cxx FumiliBuilder.cxx FumiliErrorUpdator.cxx FumiliGradientCalculator.cxx FumiliMinimizer.cxx FumiliStandardChi2FCN.cxx FumiliStandardMaximumLikelihoodFCN.cxx HessianGradientCalculator.cxx InitialGradientCalculator.cxx LaEigenValues.cxx LaInnerProduct.cxx LaInverse.cxx LaOuterProduct.cxx LaSumOfElements.cxx LaVtMVSimilarity.cxx MPIProcess.cxx MinimumBuilder.cxx Minuit2Minimizer.cxx MnApplication.cxx MnContours.cxx MnCovarianceSqueeze.cxx MnEigen.cxx MnFcn.cxx MnFumiliMinimize.cxx MnFunctionCross.cxx MnGlobalCorrelationCoeff.cxx MnHesse.cxx MnLineSearch.cxx MnMachinePrecision.cxx MnMinos.cxx MnParabolaFactory.cxx MnParameterScan.cxx MnPlot.cxx MnPosDef.cxx MnPrint.cxx MnPrintImpl.cxx MnScan.cxx MnSeedGenerator.cxx MnStrategy.cxx MnTiny.cxx MnTraceObject.cxx MnUserFcn.cxx MnUserParameterState.cxx MnUserParameters.cxx MnUserTransformation.cxx ModularFunctionMinimizer.cxx NegativeG2LineSearch.cxx Numerical2PGradientCalculator.cxx ParametricFunction.cxx ScanBuilder.cxx SimplexBuilder.cxx SimplexParameters.cxx SimplexSeedGenerator.cxx SinParameterTransformation.cxx SqrtLowParameterTransformation.cxx SqrtUpParameterTransformation.cxx VariableMetricBuilder.cxx VariableMetricEDMEstimator.cxx mnbins.cxx mndasum.cxx mndaxpy.cxx mnddot.cxx mndscal.cxx mndspmv.cxx mndspr.cxx mnlsame.cxx mnteigen.cxx mntplot.cxx mnvert.cxx mnxerbla.cxx ) prepend_path(MINUIT2_HEADERS "${Minuit2_SOURCE_DIR}/inc/Minuit2" ${MINUIT2_HEADERS}) prepend_path(MINUIT2_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}" ${MINUIT2_SOURCES}) add_library(Minuit2 ${MINUIT2_SOURCES} ${MINUIT2_HEADERS} ) # Add alias for direct inclusion with add_subdirectory add_library(Minuit2::Minuit2 ALIAS Minuit2) target_include_directories( Minuit2 PUBLIC $ $ ) target_compile_features(Minuit2 PUBLIC cxx_nullptr cxx_nonstatic_member_init) set_target_properties(Minuit2 PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries(Minuit2 PUBLIC Minuit2Math Minuit2Common) install(TARGETS Minuit2 EXPORT Minuit2Targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) install(FILES ${MINUIT2_HEADERS} DESTINATION include/Minuit2/Minuit2) iminuit-2.24.0/extern/root/math/minuit2/src/CombinedMinimumBuilder.cxx0000644000000000000000000000357114332717401022721 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/CombinedMinimumBuilder.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { FunctionMinimum CombinedMinimumBuilder::Minimum(const MnFcn &fcn, const GradientCalculator &gc, const MinimumSeed &seed, const MnStrategy &strategy, unsigned int maxfcn, double edmval) const { // find minimum using combined method // (Migrad then if fails try Simplex and then Migrad again) MnPrint print("CombinedMinimumBuilder"); FunctionMinimum min = fVMMinimizer.Builder().Minimum(fcn, gc, seed, strategy, maxfcn, edmval); if (!min.IsValid()) { print.Warn("Migrad method fails, will try with simplex method first"); MnStrategy str(2); FunctionMinimum min1 = fSimplexMinimizer.Builder().Minimum(fcn, gc, seed, str, maxfcn, edmval); if (!min1.IsValid()) { print.Warn("Both Migrad and Simplex methods failed"); return min1; } MinimumSeed seed1 = fVMMinimizer.SeedGenerator()(fcn, gc, min1.UserState(), str); FunctionMinimum min2 = fVMMinimizer.Builder().Minimum(fcn, gc, seed1, str, maxfcn, edmval); if (!min2.IsValid()) { print.Warn("Both migrad and method failed also at 2nd attempt; return simplex Minimum"); return min1; } return min2; } return min; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/DavidonErrorUpdator.cxx0000644000000000000000000001144014332717401022265 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/DavidonErrorUpdator.h" #include "Minuit2/MinimumState.h" #include "Minuit2/LaSum.h" #include "Minuit2/LaProd.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { double inner_product(const LAVector &, const LAVector &); double similarity(const LAVector &, const LASymMatrix &); double sum_of_elements(const LASymMatrix &); MinimumError DavidonErrorUpdator::Update(const MinimumState &s0, const MinimumParameters &p1, const FunctionGradient &g1) const { // update of the covarianze matrix (Davidon formula, see Tutorial, par. 4.8 pag 26) // in case of delgam > gvg (PHI > 1) use rank one formula // see par 4.10 pag 30 MnPrint print("DavidonErrorUpdator"); const MnAlgebraicSymMatrix &v0 = s0.Error().InvHessian(); MnAlgebraicVector dx = p1.Vec() - s0.Vec(); MnAlgebraicVector dg = g1.Vec() - s0.Gradient().Vec(); double delgam = inner_product(dx, dg); double gvg = similarity(dg, v0); print.Debug("\ndx", dx, "\ndg", dg, "\ndelgam", delgam, "gvg", gvg); if (delgam == 0) { print.Warn("delgam = 0 : cannot update - return same matrix"); return s0.Error(); } if (delgam < 0) { print.Warn("delgam < 0 : first derivatives increasing along search line"); } if (gvg <= 0) { // since v0 is pos def this gvg can be only = 0 if dg = 0 - should never be here print.Warn("gvg <= 0 : cannot update - return same matrix"); return s0.Error(); } MnAlgebraicVector vg = v0 * dg; MnAlgebraicSymMatrix vUpd = Outer_product(dx) / delgam - Outer_product(vg) / gvg; if (delgam > gvg) { // use rank 1 formula vUpd += gvg * Outer_product(MnAlgebraicVector(dx / delgam - vg / gvg)); } double sum_upd = sum_of_elements(vUpd); vUpd += v0; double dcov = 0.5 * (s0.Error().Dcovar() + sum_upd / sum_of_elements(vUpd)); return MinimumError(vUpd, dcov); } /* MinimumError DavidonErrorUpdator::Update(const MinimumState& s0, const MinimumParameters& p1, const FunctionGradient& g1) const { const MnAlgebraicSymMatrix& v0 = s0.Error().InvHessian(); MnAlgebraicVector dx = p1.Vec() - s0.Vec(); MnAlgebraicVector dg = g1.Vec() - s0.Gradient().Vec(); double delgam = inner_product(dx, dg); double gvg = similarity(dg, v0); // std::cout<<"delgam= "< par_vec; par_vec.resize(par.Vec().size()); for (std::size_t ix = 0; ix < par.Vec().size(); ++ix) { par_vec[ix] = par.Vec()(ix); } std::vector grad = fGradCalc.Gradient(par_vec); assert(grad.size() == fTransformation.Parameters().size()); MnAlgebraicVector v(par.Vec().size()); for (unsigned int i = 0; i < par.Vec().size(); i++) { unsigned int ext = fTransformation.ExtOfInt(i); v(i) = grad[ext]; } MnPrint print("ExternalInternalGradientCalculator"); print.Debug("User given gradient in Minuit2", v); return FunctionGradient(v); } FunctionGradient ExternalInternalGradientCalculator::operator()(const MinimumParameters &par, const FunctionGradient &functionGradient) const { std::vector par_vec; par_vec.resize(par.Vec().size()); for (std::size_t ix = 0; ix < par.Vec().size(); ++ix) { par_vec[ix] = par.Vec()(ix); } std::vector previous_grad(functionGradient.Grad().Data(), functionGradient.Grad().Data() + functionGradient.Grad().size()); std::vector previous_g2(functionGradient.G2().Data(), functionGradient.G2().Data() + functionGradient.G2().size()); std::vector previous_gstep(functionGradient.Gstep().Data(), functionGradient.Gstep().Data() + functionGradient.Gstep().size()); std::vector grad = fGradCalc.GradientWithPrevResult(par_vec, previous_grad.data(), previous_g2.data(), previous_gstep.data()); assert(grad.size() == fTransformation.Parameters().size()); MnAlgebraicVector v(par.Vec().size()); MnAlgebraicVector v_g2(par.Vec().size()); MnAlgebraicVector v_gstep(par.Vec().size()); for (unsigned int i = 0; i < par.Vec().size(); i++) { unsigned int ext = fTransformation.ExtOfInt(i); v(i) = grad[ext]; v_g2(i) = previous_g2[ext]; v_gstep(i) = previous_gstep[ext]; } MnPrint print("ExternalInternalGradientCalculator"); print.Debug("User given gradient in Minuit2", v, "g2", v_g2, "step size", v_gstep); return FunctionGradient(v, v_g2, v_gstep); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/FumiliBuilder.cxx0000644000000000000000000003044214332717401021067 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/FumiliBuilder.h" #include "Minuit2/FumiliStandardMaximumLikelihoodFCN.h" #include "Minuit2/GradientCalculator.h" //#include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MinimumError.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnLineSearch.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnPosDef.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/LaSum.h" #include "Minuit2/LaProd.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnHesse.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { double inner_product(const LAVector &, const LAVector &); FunctionMinimum FumiliBuilder::Minimum(const MnFcn &fcn, const GradientCalculator &gc, const MinimumSeed &seed, const MnStrategy &strategy, unsigned int maxfcn, double edmval) const { // top level function to find minimum from a given initial seed // iterate on a minimum search in case of first attempt is not successful MnPrint print("FumiliBuilder", PrintLevel()); edmval *= 0.0001; // edmval *= 0.1; // use small factor for Fumili print.Debug("Convergence when edm <", edmval); if (seed.Parameters().Vec().size() == 0) { return FunctionMinimum(seed, fcn.Up()); } // double edm = Estimator().Estimate(seed.Gradient(), seed.Error()); double edm = seed.State().Edm(); FunctionMinimum min(seed, fcn.Up()); if (edm < 0.) { print.Warn("Initial matrix not pos.def."); return min; } std::vector result; // result.reserve(1); result.reserve(8); result.push_back(seed.State()); print.Info("Start iterating until Edm is <", edmval, '\n', "Initial state", MnPrint::Oneline(seed.State())); if (TraceIter()) TraceIteration(result.size() - 1, result.back()); // do actual iterations // try first with a maxfxn = 50% of maxfcn // Fumili in principle needs much less iterations int maxfcn_eff = int(0.5 * maxfcn); int ipass = 0; double edmprev = 1; do { min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval); // second time check for validity of function Minimum if (ipass > 0) { if (!min.IsValid()) { print.Warn("FunctionMinimum is invalid"); return min; } } // resulting edm of minimization edm = result.back().Edm(); print.Debug("Approximate Edm", edm, "npass", ipass); // call always Hesse (error matrix from Fumili is never accurate since is approximate) print.Debug("FumiliBuilder will verify convergence and Error matrix; " "dcov", min.Error().Dcovar()); // // recalculate the gradient using the numerical gradient calculator // Numerical2PGradientCalculator ngc(fcn, min.Seed().Trafo(), strategy); // FunctionGradient ng = ngc( min.State().Parameters() ); // MinimumState tmp( min.State().Parameters(), min.State().Error(), ng, min.State().Edm(), // min.State().NFcn() ); MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(), maxfcn); result.push_back(st); print.Info("After Hessian"); if (TraceIter()) TraceIteration(result.size() - 1, result.back()); // check edm edm = st.Edm(); print.Debug("Edm", edm, "State", st); // break the loop if edm is NOT getting smaller if (ipass > 0 && edm >= edmprev) { print.Warn("Stop iterations, no improvements after Hesse; current Edm", edm, "previous value", edmprev); break; } if (edm > edmval) { print.Debug("Tolerance not sufficient, continue minimization; Edm", edm, "Requested", edmval); } else { // Case when edm < edmval after Heasse but min is flagged eith a bad edm: // make then a new Function minimum since now edm is ok if (min.IsAboveMaxEdm()) { min = FunctionMinimum(min.Seed(), min.States(), min.Up()); break; } } // end loop on iterations // ? need a maximum here (or max of function calls is enough ? ) // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient) // no need to check that hesse calculation is done (if isnot done edm is OK anyway) // count the pass to exit second time when function Minimum is invalid // increase by 20% maxfcn for doing some more tests if (ipass == 0) maxfcn_eff = maxfcn; ipass++; edmprev = edm; } while (edm > edmval); // Add latest state (Hessian calculation) min.Add(result.back()); return min; } FunctionMinimum FumiliBuilder::Minimum(const MnFcn &fcn, const GradientCalculator &gc, const MinimumSeed &seed, std::vector &result, unsigned int maxfcn, double edmval) const { // function performing the minimum searches using the FUMILI algorithm // after the modification when I iterate on this functions, so it can be called many times, // the seed is used here only to get precision and construct the returned FunctionMinimum object /* Three options were possible: 1) create two parallel and completely separate hierarchies, in which case the FumiliMinimizer would NOT inherit from ModularFunctionMinimizer, FumiliBuilder would not inherit from MinimumBuilder etc 2) Use the inheritance (base classes of ModularFunctionMinimizer, MinimumBuilder etc), but recreate the member functions Minimize() and Minimum() respectively (naming them for example minimize2() and minimum2()) so that they can take FumiliFCNBase as Parameter instead FCNBase (otherwise one wouldn't be able to call the Fumili-specific methods). 3) Cast in the daughter classes derived from ModularFunctionMinimizer, MinimumBuilder. The first two would mean to duplicate all the functionality already existent, which is a very bad practice and Error-prone. The third one is the most elegant and effective solution, where the only constraint is that the user must know that they have to pass a subclass of FumiliFCNBase to the FumiliMinimizer and not just a subclass of FCNBase. BTW, the first two solutions would have meant to recreate also a parallel structure for MnFcn... **/ // const FumiliFCNBase* tmpfcn = dynamic_cast(&(fcn.Fcn())); const MnMachinePrecision &prec = seed.Precision(); const MinimumState &initialState = result.back(); double edm = initialState.Edm(); MnPrint print("FumiliBuilder"); print.Debug("Initial State:", "\n Parameter", initialState.Vec(), "\n Gradient", initialState.Gradient().Vec(), "\n Inv Hessian", initialState.Error().InvHessian(), "\n edm", initialState.Edm(), "\n maxfcn", maxfcn, "\n tolerance", edmval); // iterate until edm is small enough or max # of iterations reached edm *= (1. + 3. * initialState.Error().Dcovar()); MnAlgebraicVector step(initialState.Gradient().Vec().size()); // initial lambda Value double lambda = 0.001; do { // const MinimumState& s0 = result.back(); MinimumState s0 = result.back(); step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec(); print.Debug("Iteration -", result.size(), "\n Fval", s0.Fval(), "numOfCall", fcn.NumOfCalls(), "\n Internal Parameter values", s0.Vec(), "\n Newton step", step); double gdel = inner_product(step, s0.Gradient().Grad()); if (gdel > 0.) { print.Warn("Matrix not pos.def, gdel =", gdel, " > 0"); MnPosDef psdf; s0 = psdf(s0, prec); step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec(); gdel = inner_product(step, s0.Gradient().Grad()); print.Warn("After correction, gdel =", gdel); if (gdel > 0.) { result.push_back(s0); return FunctionMinimum(seed, result, fcn.Up()); } } // MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec); // if(std::fabs(pp.Y() - s0.Fval()) < prec.Eps()) { // std::cout<<"FumiliBuilder: no improvement"<= s0.Fval()) { MnLineSearch lsearch; MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec); if (std::fabs(pp.Y() - s0.Fval()) < prec.Eps()) { // std::cout<<"FumiliBuilder: no improvement"< edmval) { if (edm < std::fabs(prec.Eps2() * result.back().Fval())) { print.Warn("Machine accuracy limits further improvement"); return FunctionMinimum(seed, result, fcn.Up()); } else if (edm < 10 * edmval) { return FunctionMinimum(seed, result, fcn.Up()); } else { print.Warn("No convergence; Edm", edm, "is above tolerance", 10 * edmval); return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnAboveMaxEdm); } } // std::cout<<"result.back().Error().Dcovar()= "< namespace ROOT { namespace Minuit2 { double sum_of_elements(const LASymMatrix &); MinimumError FumiliErrorUpdator::Update(const MinimumState &s0, const MinimumParameters &p1, const FunctionGradient &g1) const { // dummy methods to suppress unused variable warnings // this member function should never be called within // the Fumili method... s0.Fval(); p1.Fval(); g1.IsValid(); return MinimumError(2); } MinimumError FumiliErrorUpdator::Update(const MinimumState &s0, const MinimumParameters &p1, const GradientCalculator &gc, double lambda) const { // calculate the error matrix using approximation of Fumili // use only first derivatives (see tutorial par. 5.1,5.2) // The Fumili Hessian is provided by the FumiliGRadientCalculator class // we apply also the Marquard lambda factor to increase weight of diagonal term // as suggester in Numerical Receipt for Marquard method // need to downcast to FumiliGradientCalculator FumiliGradientCalculator *fgc = dynamic_cast(const_cast(&gc)); assert(fgc != 0); MnPrint print("FumiliErrorUpdator"); // get Hessian from Gradient calculator MnAlgebraicSymMatrix h = fgc->Hessian(); int nvar = p1.Vec().size(); // apply Marquard lambda factor double eps = 8 * std::numeric_limits::min(); for (int j = 0; j < nvar; j++) { h(j, j) *= (1. + lambda); // if h(j,j) is zero what to do ? if (std::fabs(h(j, j)) < eps) { // should use DBL_MIN // put a cut off to avoid zero on diagonals if (lambda > 1) h(j, j) = lambda * eps; else h(j, j) = eps; } } int ifail = Invert(h); if (ifail != 0) { print.Warn("inversion fails; return diagonal matrix"); for (unsigned int i = 0; i < h.Nrow(); i++) { h(i, i) = 1. / h(i, i); } } const MnAlgebraicSymMatrix &v0 = s0.Error().InvHessian(); // calculate by how much did the covariance matrix changed // (if it changed a lot since the last step, probably we // are not yet near the Minimum) double dcov = 0.5 * (s0.Error().Dcovar() + sum_of_elements(h - v0) / sum_of_elements(h)); return MinimumError(h, dcov); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/FumiliGradientCalculator.cxx0000644000000000000000000000760714332717401023257 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/FumiliGradientCalculator.h" #include "Minuit2/FumiliFCNBase.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/FumiliChi2FCN.h" #include "Minuit2/FumiliMaximumLikelihoodFCN.h" #include "Minuit2/MnPrint.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnUserFcn.h" namespace ROOT { namespace Minuit2 { FunctionGradient FumiliGradientCalculator::operator()(const MinimumParameters &par) const { // Calculate gradient for Fumili using the gradient and Hessian provided by the FCN Fumili function // applying the external to int trasformation. int nvar = par.Vec().size(); std::vector extParam = fTransformation(par.Vec()); // std::vector deriv; // deriv.reserve( nvar ); // for (int i = 0; i < nvar; ++i) { // unsigned int ext = fTransformation.ExtOfInt(i); // if ( fTransformation.Parameter(ext).HasLimits()) // deriv.push_back( fTransformation.DInt2Ext( i, par.Vec()(i) ) ); // else // deriv.push_back(1.0); // } // eval Gradient FumiliFCNBase &fcn = const_cast(fFcn); fcn.EvaluateAll(extParam); MnAlgebraicVector v(nvar); MnAlgebraicSymMatrix h(nvar); const std::vector &fcn_gradient = fFcn.Gradient(); assert(fcn_gradient.size() == extParam.size()); // for (int i = 0; i < nvar; ++i) { // unsigned int iext = fTransformation.ExtOfInt(i); // double ideriv = 1.0; // if ( fTransformation.Parameter(iext).HasLimits()) // ideriv = fTransformation.DInt2Ext( i, par.Vec()(i) ) ; // // v(i) = fcn_gradient[iext]*deriv; // v(i) = ideriv*fcn_gradient[iext]; // for (int j = i; j < nvar; ++j) { // unsigned int jext = fTransformation.ExtOfInt(j); // double jderiv = 1.0; // if ( fTransformation.Parameter(jext).HasLimits()) // jderiv = fTransformation.DInt2Ext( j, par.Vec()(j) ) ; // // h(i,j) = deriv[i]*deriv[j]*fFcn.Hessian(iext,jext); // h(i,j) = ideriv*jderiv*fFcn.Hessian(iext,jext); // } // } // cache deriv and Index values . // in large Parameter limit then need to re-optimize and see if better not caching std::vector deriv(nvar); std::vector extIndex(nvar); for (int i = 0; i < nvar; ++i) { extIndex[i] = fTransformation.ExtOfInt(i); deriv[i] = 1; if (fTransformation.Parameter(extIndex[i]).HasLimits()) deriv[i] = fTransformation.DInt2Ext(i, par.Vec()(i)); v(i) = fcn_gradient[extIndex[i]] * deriv[i]; for (int j = 0; j <= i; ++j) { h(i, j) = deriv[i] * deriv[j] * fFcn.Hessian(extIndex[i], extIndex[j]); } } MnPrint print("FumiliGradientCalculator"); print.Debug([&](std::ostream &os) { // compare Fumili with Minuit gradient Numerical2PGradientCalculator gc(MnUserFcn(fFcn, fTransformation), fTransformation, MnStrategy(1)); FunctionGradient g2 = gc(par); os << "Fumili Gradient" << v << "\nMinuit Gradient" << g2.Vec(); }); // store calculated Hessian fHessian = h; return FunctionGradient(v); } FunctionGradient FumiliGradientCalculator::operator()(const MinimumParameters &par, const FunctionGradient &) const { // Needed for interface of base class. return this->operator()(par); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/FumiliMinimizer.cxx0000644000000000000000000000717214332717401021450 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnConfig.h" #include "Minuit2/FumiliMinimizer.h" #include "Minuit2/MinimumSeedGenerator.h" #include "Minuit2/FumiliGradientCalculator.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/AnalyticalGradientCalculator.h" #include "Minuit2/MinimumBuilder.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnUserParameters.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnUserFcn.h" #include "Minuit2/FumiliFCNBase.h" #include "Minuit2/FCNGradientBase.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { // for Fumili implement Minimize here because need downcast FunctionMinimum FumiliMinimizer::Minimize(const FCNBase &fcn, const MnUserParameterState &st, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // Minimize using Fumili. Create seed and Fumili gradient calculator. // The FCNBase passed must be a FumiliFCNBase type otherwise method will fail ! MnPrint print("FumiliMinimizer"); MnUserFcn mfcn(fcn, st.Trafo()); Numerical2PGradientCalculator gc(mfcn, st.Trafo(), strategy); unsigned int npar = st.VariableParameters(); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; // FUMILI needs much less function calls maxfcn = int(0.1 * maxfcn); MinimumSeed mnseeds = SeedGenerator()(mfcn, gc, st, strategy); // downcast fcn // std::cout << "FCN type " << typeid(&fcn).Name() << std::endl; FumiliFCNBase *fumiliFcn = dynamic_cast(const_cast(&fcn)); if (!fumiliFcn) { print.Error("Wrong FCN type; try to use default minimizer"); return FunctionMinimum(mnseeds, fcn.Up()); } FumiliGradientCalculator fgc(*fumiliFcn, st.Trafo(), npar); print.Debug("Using FumiliMinimizer"); return ModularFunctionMinimizer::Minimize(mfcn, fgc, mnseeds, strategy, maxfcn, toler); } FunctionMinimum FumiliMinimizer::Minimize(const FCNGradientBase &fcn, const MnUserParameterState &st, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { MnPrint print("FumiliMinimizer::Minimize"); // Minimize using Fumili. Case of interface is a FCNGradientBase. // Normally other method is used - probably this could be removed (t.b.i.) // need MnUserFcn MnUserFcn mfcn(fcn, st.Trafo()); AnalyticalGradientCalculator gc(fcn, st.Trafo()); unsigned int npar = st.VariableParameters(); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; MinimumSeed mnseeds = SeedGenerator()(mfcn, gc, st, strategy); // downcast fcn FumiliFCNBase *fumiliFcn = dynamic_cast(const_cast(&fcn)); if (!fumiliFcn) { print.Error("Wrong FCN type; try to use default minimizer"); return FunctionMinimum(mnseeds, fcn.Up()); } FumiliGradientCalculator fgc(*fumiliFcn, st.Trafo(), npar); print.Debug("Using FumiliMinimizer"); return ModularFunctionMinimizer::Minimize(mfcn, fgc, mnseeds, strategy, maxfcn, toler); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/FumiliStandardChi2FCN.cxx0000644000000000000000000000663314332717401022303 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/FumiliStandardChi2FCN.h" #include #include namespace ROOT { namespace Minuit2 { std::vector FumiliStandardChi2FCN::Elements(const std::vector &par) const { // Calculate the f(i) contribution to the Chi2. Chi2 = Sum[f(i)**2] std::vector result; double tmp1 = 0.0; unsigned int fPositionsSize = fPositions.size(); for (unsigned int i = 0; i < fPositionsSize; i++) { const std::vector ¤tPosition = fPositions[i]; // The commented line is the object-oriented way to do it // but it is faster to do a single function call... //(*(this->getModelFunction())).SetParameters(par); tmp1 = (*(this->ModelFunction()))(par, currentPosition) - fMeasurements[i]; result.push_back(tmp1 * fInvErrors[i]); // std::cout << "element " << i << " " << (*(this->getModelFunction()))(par, currentPosition) << " " << // fMeasurements[i] << " " << result[i] << std::endl; } return result; } const std::vector &FumiliStandardChi2FCN::GetMeasurement(int index) const { // Return the coordinate (position) values. return fPositions[index]; } int FumiliStandardChi2FCN::GetNumberOfMeasurements() const { // Return size return fPositions.size(); } void FumiliStandardChi2FCN::EvaluateAll(const std::vector &par) { // Evaluate chi2 value, gradient and hessian all in a single // loop on the measurements int nmeas = GetNumberOfMeasurements(); std::vector &grad = Gradient(); std::vector &h = Hessian(); int npar = par.size(); double chi2 = 0; grad.resize(npar); h.resize(static_cast(0.5 * npar * (npar + 1))); // reset Elements grad.assign(npar, 0.0); h.assign(static_cast(0.5 * npar * (npar + 1)), 0.0); const ParametricFunction &modelFunc = *ModelFunction(); for (int i = 0; i < nmeas; ++i) { // work for multi-dimensional points const std::vector ¤tPosition = fPositions[i]; modelFunc.SetParameters(currentPosition); double invError = fInvErrors[i]; double fval = modelFunc(par); double element = (fval - fMeasurements[i]) * invError; chi2 += element * element; // calc derivatives // this method should return a reference std::vector mfg = modelFunc.GetGradient(par); // grad is derivative of chi2 w.r.t to parameters for (int j = 0; j < npar; ++j) { double dfj = invError * mfg[j]; grad[j] += 2.0 * element * dfj; // in second derivative use Fumili approximation neglecting the term containing the // second derivatives of the model function for (int k = j; k < npar; ++k) { int idx = j + k * (k + 1) / 2; h[idx] += 2.0 * dfj * invError * mfg[k]; } } // end param loop } // end points loop // set Value in base class SetFCNValue(chi2); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/FumiliStandardMaximumLikelihoodFCN.cxx0000644000000000000000000001344314332717401025134 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/FumiliStandardMaximumLikelihoodFCN.h" #include #include #include namespace ROOT { namespace Minuit2 { std::vector FumiliStandardMaximumLikelihoodFCN::Elements(const std::vector &par) const { // calculate likelihood element f(i) = pdf(x(i)) std::vector result; double tmp1 = 0.0; unsigned int fPositionsSize = fPositions.size(); for (unsigned int i = 0; i < fPositionsSize; i++) { const std::vector ¤tPosition = fPositions[i]; // The commented line is the object-oriented way to do it // but it is faster to do a single function call... //(*(this->getModelFunction())).SetParameters(par); tmp1 = (*(this->ModelFunction()))(par, currentPosition); // std::cout << " i = " << i << " " << currentPosition[0] << " " << tmp1 << std::endl; result.push_back(tmp1); } return result; } const std::vector &FumiliStandardMaximumLikelihoodFCN::GetMeasurement(int index) const { // Return x(i). return fPositions[index]; } int FumiliStandardMaximumLikelihoodFCN::GetNumberOfMeasurements() const { // return size of positions (coordinates). return fPositions.size(); } void FumiliStandardMaximumLikelihoodFCN::EvaluateAll(const std::vector &par) { // Evaluate in one loop likelihood value, gradient and hessian const double minDouble = 8.0 * std::numeric_limits::min(); const double minDouble2 = std::sqrt(minDouble); const double maxDouble2 = 1.0 / minDouble2; // loop on the measurements int nmeas = GetNumberOfMeasurements(); std::vector &grad = Gradient(); std::vector &h = Hessian(); int npar = par.size(); double logLikelihood = 0; grad.resize(npar); h.resize(static_cast(0.5 * npar * (npar + 1))); grad.assign(npar, 0.0); h.assign(static_cast(0.5 * npar * (npar + 1)), 0.0); const ParametricFunction &modelFunc = *ModelFunction(); for (int i = 0; i < nmeas; ++i) { // work for one-dimensional points const std::vector ¤tPosition = fPositions[i]; modelFunc.SetParameters(currentPosition); double fval = modelFunc(par); if (fval < minDouble) fval = minDouble; // to avoid getting infinity and nan's logLikelihood -= std::log(fval); double invFval = 1.0 / fval; // this method should return a reference std::vector mfg = modelFunc.GetGradient(par); // calc derivatives for (int j = 0; j < npar; ++j) { if (std::fabs(mfg[j]) < minDouble) { // std::cout << "SMALL values: grad = " << mfg[j] << " " << minDouble << " f(x) = " << fval // << " params " << j << " p0 = " << par[0] << " p1 = " << par[1] << std::endl; if (mfg[j] < 0) mfg[j] = -minDouble; else mfg[j] = minDouble; } double dfj = invFval * mfg[j]; // to avoid summing infinite and nan later when calculating the Hessian if (std::fabs(dfj) > maxDouble2) { if (dfj > 0) dfj = maxDouble2; else dfj = -maxDouble2; } grad[j] -= dfj; // if ( ! ( dfj > 0) && ! ( dfj <= 0 ) ) // std::cout << " nan : dfj = " << dfj << " fval = " << fval << " invF = " << invFval << " grad = " << mfg[j] // << " par[j] = " << par[j] << std::endl; // std::cout << " x = " << currentPosition[0] << " par[j] = " << par[j] << " : dfj = " << dfj << " fval = " // << fval << " invF = " << invFval << " grad = " << mfg[j] << " deriv = " << grad[j] << std::endl; // in second derivative use Fumili approximation neglecting the term containing the // second derivatives of the model function for (int k = j; k < npar; ++k) { int idx = j + k * (k + 1) / 2; if (std::fabs(mfg[k]) < minDouble) { if (mfg[k] < 0) mfg[k] = -minDouble; else mfg[k] = minDouble; } double dfk = invFval * mfg[k]; // avoid that dfk*dfj are one small and one huge so I get a nan // to avoid summing infinite and nan later when calculating the Hessian if (std::fabs(dfk) > maxDouble2) { if (dfk > 0) dfk = maxDouble2; else dfk = -maxDouble2; } h[idx] += dfj * dfk; // if ( ( ! ( h[idx] > 0) && ! ( h[idx] <= 0 ) ) ) // std::cout << " nan : dfj = " << dfj << " fval = " << fval << " invF = " << invFval << " gradj = " << // mfg[j] // << " dfk = " << dfk << " gradk = "<< mfg[k] << " hess_jk = " << h[idx] << " par[k] = " << par[k] << // std::endl; } } // end param loop } // end points loop // std::cout <<"\nEVALUATED GRADIENT and HESSIAN " << std::endl; // for (int j = 0; j < npar; ++j) { // std::cout << " j = " << j << " grad = " << grad[j] << std::endl; // for (int k = j; k < npar; ++k) { // std::cout << " k = " << k << " hess = " << Hessian(j,k) << " " << h[ j + k*(k+1)/2] << std::endl; // } // } // set Value in base class SetFCNValue(logLikelihood); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/HessianGradientCalculator.cxx0000644000000000000000000001155014332717401023414 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/HessianGradientCalculator.h" #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnPrint.h" #include "Minuit2/MPIProcess.h" #include #include namespace ROOT { namespace Minuit2 { FunctionGradient HessianGradientCalculator::operator()(const MinimumParameters &par) const { // use initial gradient as starting point InitialGradientCalculator gc(fFcn, fTransformation, fStrategy); FunctionGradient gra = gc(par); return (*this)(par, gra); } FunctionGradient HessianGradientCalculator:: operator()(const MinimumParameters &par, const FunctionGradient &Gradient) const { // interface of the base class. Use DeltaGradient for op. std::pair mypair = DeltaGradient(par, Gradient); return mypair.first; } const MnMachinePrecision &HessianGradientCalculator::Precision() const { // return the precision return fTransformation.Precision(); } unsigned int HessianGradientCalculator::Ncycle() const { // return number of calculation cycles (defined in strategy) return Strategy().HessianGradientNCycles(); } double HessianGradientCalculator::StepTolerance() const { // return tolerance on step size (defined in strategy) return Strategy().GradientStepTolerance(); } double HessianGradientCalculator::GradTolerance() const { // return gradient tolerance (defines in strategy) return Strategy().GradientTolerance(); } std::pair HessianGradientCalculator::DeltaGradient(const MinimumParameters &par, const FunctionGradient &Gradient) const { // calculate gradient for Hessian assert(par.IsValid()); MnPrint print("HessianGradientCalculator"); MnAlgebraicVector x = par.Vec(); MnAlgebraicVector grd = Gradient.Grad(); const MnAlgebraicVector &g2 = Gradient.G2(); // const MnAlgebraicVector& gstep = Gradient.Gstep(); // update also gradient step sizes MnAlgebraicVector gstep = Gradient.Gstep(); double fcnmin = par.Fval(); // std::cout<<"fval: "< optstp) d = optstp; if (d < dmin) d = dmin; double chgold = 10000.; double dgmin = 0.; double grdold = 0.; double grdnew = 0.; for (unsigned int j = 0; j < Ncycle(); j++) { x(i) = xtf + d; double fs1 = Fcn()(x); x(i) = xtf - d; double fs2 = Fcn()(x); x(i) = xtf; // double sag = 0.5*(fs1+fs2-2.*fcnmin); // LM: should I calculate also here second derivatives ??? grdold = grd(i); grdnew = (fs1 - fs2) / (2. * d); dgmin = Precision().Eps() * (std::fabs(fs1) + std::fabs(fs2)) / d; // if(fabs(grdnew) < Precision().Eps()) break; if (grdnew == 0) break; double change = fabs((grdold - grdnew) / grdnew); if (change > chgold && j > 1) break; chgold = change; grd(i) = grdnew; // LM : update also the step sizes gstep(i) = d; if (change < 0.05) break; if (fabs(grdold - grdnew) < dgmin) break; if (d < dmin) break; d *= 0.2; } dgrd(i) = std::max(dgmin, std::fabs(grdold - grdnew)); print.Debug("HGC Param :", i, "\t new g1 =", grd(i), "gstep =", d, "dgrd =", dgrd(i)); } mpiproc.SyncVector(grd); mpiproc.SyncVector(gstep); mpiproc.SyncVector(dgrd); return std::pair(FunctionGradient(grd, g2, gstep), dgrd); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/InitialGradientCalculator.cxx0000644000000000000000000000727214332717401023421 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { FunctionGradient InitialGradientCalculator::operator()(const MinimumParameters &par) const { // initial rough estimate of the gradient using the parameter step size assert(par.IsValid()); unsigned int n = Trafo().VariableParameters(); assert(n == par.Vec().size()); MnPrint print("InitialGradientCalculator"); print.Debug("Calculating initial gradient at point", par.Vec()); MnAlgebraicVector gr(n), gr2(n), gst(n); for (unsigned int i = 0; i < n; i++) { unsigned int exOfIn = Trafo().ExtOfInt(i); double var = par.Vec()(i); double werr = Trafo().Parameter(exOfIn).Error(); double sav = Trafo().Int2ext(i, var); double sav2 = sav + werr; if (Trafo().Parameter(exOfIn).HasLimits()) { if (Trafo().Parameter(exOfIn).HasUpperLimit() && sav2 > Trafo().Parameter(exOfIn).UpperLimit()) sav2 = Trafo().Parameter(exOfIn).UpperLimit(); } double var2 = Trafo().Ext2int(exOfIn, sav2); double vplu = var2 - var; sav2 = sav - werr; if (Trafo().Parameter(exOfIn).HasLimits()) { if (Trafo().Parameter(exOfIn).HasLowerLimit() && sav2 < Trafo().Parameter(exOfIn).LowerLimit()) sav2 = Trafo().Parameter(exOfIn).LowerLimit(); } var2 = Trafo().Ext2int(exOfIn, sav2); double vmin = var2 - var; double gsmin = 8. * Precision().Eps2() * (std::fabs(var) + Precision().Eps2()); // protect against very small step sizes which can cause dirin to zero and then nan values in grd double dirin = std::max(0.5 * (std::fabs(vplu) + std::fabs(vmin)), gsmin); double g2 = 2.0 * fFcn.ErrorDef() / (dirin * dirin); double gstep = std::max(gsmin, 0.1 * dirin); double grd = g2 * dirin; if (Trafo().Parameter(exOfIn).HasLimits()) { if (gstep > 0.5) gstep = 0.5; } gr(i) = grd; gr2(i) = g2; gst(i) = gstep; print.Debug("Computed initial gradient for parameter", Trafo().Name(exOfIn), "value", var, "[", vmin, ",", vplu, "]", "dirin", dirin, "grd", grd, "g2", g2); } return FunctionGradient(gr, gr2, gst); } FunctionGradient InitialGradientCalculator::operator()(const MinimumParameters &par, const FunctionGradient &) const { // Base class interface return (*this)(par); } const MnMachinePrecision &InitialGradientCalculator::Precision() const { // return precision (is set in trasformation class) return fTransformation.Precision(); } unsigned int InitialGradientCalculator::Ncycle() const { // return ncyles (from Strategy) return Strategy().GradientNCycles(); } double InitialGradientCalculator::StepTolerance() const { // return Gradient step tolerance (from Strategy) return Strategy().GradientStepTolerance(); } double InitialGradientCalculator::GradTolerance() const { // return Gradient tolerance return Strategy().GradientTolerance(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/LaEigenValues.cxx0000644000000000000000000000250314332717401021014 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/LAVector.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { int mneigen(double *, unsigned int, unsigned int, unsigned int, double *, double); LAVector eigenvalues(const LASymMatrix &mat) { // calculate eigenvalues of symmetric matrices using mneigen function (transalte from fortran Minuit) unsigned int nrow = mat.Nrow(); LAVector tmp(nrow * nrow); LAVector work(2 * nrow); for (unsigned int i = 0; i < nrow; i++) for (unsigned int j = 0; j <= i; j++) { tmp(i + j * nrow) = mat(i, j); tmp(i * nrow + j) = mat(i, j); } int info = mneigen(tmp.Data(), nrow, nrow, work.size(), work.Data(), 1.e-6); (void)info; assert(info == 0); LAVector result(nrow); for (unsigned int i = 0; i < nrow; i++) result(i) = work(i); return result; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/LaInnerProduct.cxx0000644000000000000000000000150614332717401021223 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/LAVector.h" namespace ROOT { namespace Minuit2 { double mnddot(unsigned int, const double *, int, const double *, int); double inner_product(const LAVector &v1, const LAVector &v2) { // calculate inner (dot) product of two vectors using mnddot function return mnddot(v1.size(), v1.Data(), 1, v2.Data(), 1); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/LaInverse.cxx0000644000000000000000000000201314332717401020214 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/LaInverse.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { int mnvert(LASymMatrix &t); // symmetric matrix (positive definite only) int Invert(LASymMatrix &t) { // function for inversion of symmetric matrices using mnvert function // (from Fortran Minuit) int ifail = 0; if (t.size() == 1) { double tmp = t.Data()[0]; if (!(tmp > 0.)) ifail = 1; else t.Data()[0] = 1. / tmp; } else { ifail = mnvert(t); } return ifail; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/LaOuterProduct.cxx0000644000000000000000000000512014332717401021242 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/LaOuterProduct.h" #include "Minuit2/LAVector.h" #include "Minuit2/LASymMatrix.h" namespace ROOT { namespace Minuit2 { int mndspr(const char *, unsigned int, double, const double *, int, double *); LASymMatrix::LASymMatrix(const ABObj, double>, double> &out) : fSize(0), fNRow(0), fData(0) { // constructor from expression based on outer product of symmetric matrices // std::cout<<"LASymMatrix::LASymMatrix(const ABObj, double>, // double>& out)"<, double>, double> &out) { // assignment operator from expression based on outer product of symmetric matrices // std::cout<<"LASymMatrix& LASymMatrix::operator=(const ABObj, double>, double>& out)"< namespace ROOT { namespace Minuit2 { unsigned int MPIProcess::fgGlobalSize = 1; unsigned int MPIProcess::fgGlobalRank = 0; // By default all procs are for X unsigned int MPIProcess::fgCartSizeX = 0; unsigned int MPIProcess::fgCartSizeY = 0; unsigned int MPIProcess::fgCartDimension = 0; bool MPIProcess::fgNewCart = true; #ifdef MPIPROC MPI::Intracomm *MPIProcess::fgCommunicator = 0; int MPIProcess::fgIndexComm = -1; // -1 for no-initialization MPI::Intracomm *MPIProcess::fgCommunicators[2] = {0}; unsigned int MPIProcess::fgIndecesComm[2] = {0}; #endif MPIProcess::MPIProcess(unsigned int nelements, unsigned int indexComm) : fNelements(nelements), fSize(1), fRank(0) { // check local requested index for communicator, valid values are 0 and 1 indexComm = (indexComm == 0) ? 0 : 1; #ifdef MPIPROC StartMPI(); if (fgGlobalSize == fgCartDimension && fgCartSizeX != fgCartDimension && fgCartSizeY != fgCartDimension) { // declare the cartesian topology if (fgCommunicator == 0 && fgIndexComm < 0 && fgNewCart) { // first call, declare the topology std::cout << "Info --> MPIProcess::MPIProcess: Declare cartesian Topology (" << fgCartSizeX << "x" << fgCartSizeY << ")" << std::endl; int color = fgGlobalRank / fgCartSizeY; int key = fgGlobalRank % fgCartSizeY; fgCommunicators[0] = new MPI::Intracomm(MPI::COMM_WORLD.Split(key, color)); // rows for Minuit fgCommunicators[1] = new MPI::Intracomm(MPI::COMM_WORLD.Split(color, key)); // columns for NLL fgNewCart = false; } fgIndexComm++; if (fgIndexComm > 1 || fgCommunicator == (&(MPI::COMM_WORLD))) { // Remember, no more than 2 dimensions in the topology! std::cerr << "Error --> MPIProcess::MPIProcess: Requiring more than 2 dimensions in the topology!" << std::endl; MPI::COMM_WORLD.Abort(-1); } // requiring columns as first call. In this case use all nodes if (((unsigned int)fgIndexComm) < indexComm) fgCommunicator = &(MPI::COMM_WORLD); else { fgIndecesComm[fgIndexComm] = indexComm; fgCommunicator = fgCommunicators[fgIndecesComm[fgIndexComm]]; } } else { // no cartesian topology if (fgCartDimension != 0 && fgGlobalSize != fgCartDimension) { std::cout << "Warning --> MPIProcess::MPIProcess: Cartesian dimension doesn't correspond to # total procs!" << std::endl; std::cout << "Warning --> MPIProcess::MPIProcess: Ignoring topology, use all procs for X." << std::endl; std::cout << "Warning --> MPIProcess::MPIProcess: Resetting topology..." << std::endl; fgCartSizeX = fgGlobalSize; fgCartSizeY = 1; fgCartDimension = fgGlobalSize; } if (fgIndexComm < 0) { if (fgCartSizeX == fgCartDimension) { fgCommunicators[0] = &(MPI::COMM_WORLD); fgCommunicators[1] = 0; } else { fgCommunicators[0] = 0; fgCommunicators[1] = &(MPI::COMM_WORLD); } } fgIndexComm++; if (fgIndexComm > 1) { // Remember, no more than 2 nested MPI calls! std::cerr << "Error --> MPIProcess::MPIProcess: More than 2 nested MPI calls!" << std::endl; MPI::COMM_WORLD.Abort(-1); } fgIndecesComm[fgIndexComm] = indexComm; // require 2 nested communicators if (fgCommunicator != 0 && fgCommunicators[indexComm] != 0) { std::cout << "Warning --> MPIProcess::MPIProcess: Requiring 2 nested MPI calls!" << std::endl; std::cout << "Warning --> MPIProcess::MPIProcess: Ignoring second call." << std::endl; fgIndecesComm[fgIndexComm] = (indexComm == 0) ? 1 : 0; } fgCommunicator = fgCommunicators[fgIndecesComm[fgIndexComm]]; } // set size and rank if (fgCommunicator != 0) { fSize = fgCommunicator->Get_size(); fRank = fgCommunicator->Get_rank(); } else { // no MPI calls fSize = 1; fRank = 0; } if (fSize > fNelements) { std::cerr << "Error --> MPIProcess::MPIProcess: more processors than elements!" << std::endl; MPI::COMM_WORLD.Abort(-1); } #endif fNumElements4JobIn = fNelements / fSize; fNumElements4JobOut = fNelements % fSize; } MPIProcess::~MPIProcess() { // destructor #ifdef MPIPROC fgCommunicator = 0; fgIndexComm--; if (fgIndexComm == 0) fgCommunicator = fgCommunicators[fgIndecesComm[fgIndexComm]]; #endif } bool MPIProcess::SyncVector(ROOT::Minuit2::MnAlgebraicVector &mnvector) { // In case of just one job, don't need sync, just go if (fSize < 2) return false; if (mnvector.size() != fNelements) { std::cerr << "Error --> MPIProcess::SyncVector: # defined elements different from # requested elements!" << std::endl; std::cerr << "Error --> MPIProcess::SyncVector: no MPI syncronization is possible!" << std::endl; exit(-1); } #ifdef MPIPROC unsigned int numElements4ThisJob = NumElements4Job(fRank); unsigned int startElementIndex = StartElementIndex(); unsigned int endElementIndex = EndElementIndex(); double dvectorJob[numElements4ThisJob]; for (unsigned int i = startElementIndex; i < endElementIndex; i++) dvectorJob[i - startElementIndex] = mnvector(i); double dvector[fNelements]; MPISyncVector(dvectorJob, numElements4ThisJob, dvector); for (unsigned int i = 0; i < fNelements; i++) { mnvector(i) = dvector[i]; } return true; #else std::cerr << "Error --> MPIProcess::SyncVector: no MPI syncronization is possible!" << std::endl; exit(-1); #endif } bool MPIProcess::SyncSymMatrixOffDiagonal(ROOT::Minuit2::MnAlgebraicSymMatrix &mnmatrix) { // In case of just one job, don't need sync, just go if (fSize < 2) return false; if (mnmatrix.size() - mnmatrix.Nrow() != fNelements) { std::cerr << "Error --> MPIProcess::SyncSymMatrixOffDiagonal: # defined elements different from # requested elements!" << std::endl; std::cerr << "Error --> MPIProcess::SyncSymMatrixOffDiagonal: no MPI syncronization is possible!" << std::endl; exit(-1); } #ifdef MPIPROC unsigned int numElements4ThisJob = NumElements4Job(fRank); unsigned int startElementIndex = StartElementIndex(); unsigned int endElementIndex = EndElementIndex(); unsigned int nrow = mnmatrix.Nrow(); unsigned int offsetVect = 0; for (unsigned int i = 0; i < startElementIndex; i++) if ((i + offsetVect) % (nrow - 1) == 0) offsetVect += (i + offsetVect) / (nrow - 1); double dvectorJob[numElements4ThisJob]; for (unsigned int i = startElementIndex; i < endElementIndex; i++) { int x = (i + offsetVect) / (nrow - 1); if ((i + offsetVect) % (nrow - 1) == 0) offsetVect += x; int y = (i + offsetVect) % (nrow - 1) + 1; dvectorJob[i - startElementIndex] = mnmatrix(x, y); } double dvector[fNelements]; MPISyncVector(dvectorJob, numElements4ThisJob, dvector); offsetVect = 0; for (unsigned int i = 0; i < fNelements; i++) { int x = (i + offsetVect) / (nrow - 1); if ((i + offsetVect) % (nrow - 1) == 0) offsetVect += x; int y = (i + offsetVect) % (nrow - 1) + 1; mnmatrix(x, y) = dvector[i]; } return true; #else std::cerr << "Error --> MPIProcess::SyncMatrix: no MPI syncronization is possible!" << std::endl; exit(-1); #endif } #ifdef MPIPROC void MPIProcess::MPISyncVector(double *ivector, int svector, double *ovector) { int offsets[fSize]; int nconts[fSize]; nconts[0] = NumElements4Job(0); offsets[0] = 0; for (unsigned int i = 1; i < fSize; i++) { nconts[i] = NumElements4Job(i); offsets[i] = nconts[i - 1] + offsets[i - 1]; } fgCommunicator->Allgatherv(ivector, svector, MPI::DOUBLE, ovector, nconts, offsets, MPI::DOUBLE); } bool MPIProcess::SetCartDimension(unsigned int dimX, unsigned int dimY) { if (fgCommunicator != 0 || fgIndexComm >= 0) { std::cout << "Warning --> MPIProcess::SetCartDimension: MPIProcess already declared! Ignoring command..." << std::endl; return false; } if (dimX * dimY <= 0) { std::cout << "Warning --> MPIProcess::SetCartDimension: Invalid topology! Ignoring command..." << std::endl; return false; } StartMPI(); if (fgGlobalSize != dimX * dimY) { std::cout << "Warning --> MPIProcess::SetCartDimension: Cartesian dimension doesn't correspond to # total procs!" << std::endl; std::cout << "Warning --> MPIProcess::SetCartDimension: Ignoring command..." << std::endl; return false; } if (fgCartSizeX != dimX || fgCartSizeY != dimY) { fgCartSizeX = dimX; fgCartSizeY = dimY; fgCartDimension = fgCartSizeX * fgCartSizeY; fgNewCart = true; if (fgCommunicators[0] != 0 && fgCommunicators[1] != 0) { delete fgCommunicators[0]; fgCommunicators[0] = 0; fgIndecesComm[0] = 0; delete fgCommunicators[1]; fgCommunicators[1] = 0; fgIndecesComm[1] = 0; } } return true; } bool MPIProcess::SetDoFirstMPICall(bool doFirstMPICall) { StartMPI(); bool ret; if (doFirstMPICall) ret = SetCartDimension(fgGlobalSize, 1); else ret = SetCartDimension(1, fgGlobalSize); return ret; } #endif #ifdef MPIPROC MPITerminate dummyMPITerminate = MPITerminate(); #endif } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MinimumBuilder.cxx0000644000000000000000000000130514332717401021251 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MinimumBuilder.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { MinimumBuilder::MinimumBuilder() : fPrintLevel(MnPrint::GlobalLevel()), fStorageLevel(1), fTracer(0) {} } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/Minuit2Minimizer.cxx0000644000000000000000000012644314332717401021555 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta Wed Oct 18 11:48:00 2006 /********************************************************************** * * * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT * * * * * **********************************************************************/ // Implementation file for class Minuit2Minimizer #include "Minuit2/Minuit2Minimizer.h" #include "Math/IFunction.h" #include "Math/IOptions.h" #include "Fit/ParameterSettings.h" #include "Minuit2/FCNAdapter.h" #include "Minuit2/FumiliFCNAdapter.h" #include "Minuit2/FCNGradAdapter.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnMigrad.h" #include "Minuit2/MnMinos.h" #include "Minuit2/MinosError.h" #include "Minuit2/MnHesse.h" #include "Minuit2/MinuitParameter.h" #include "Minuit2/MnUserFcn.h" #include "Minuit2/MnPrint.h" #include "Minuit2/VariableMetricMinimizer.h" #include "Minuit2/SimplexMinimizer.h" #include "Minuit2/CombinedMinimizer.h" #include "Minuit2/ScanMinimizer.h" #include "Minuit2/FumiliMinimizer.h" #include "Minuit2/MnParameterScan.h" #include "Minuit2/MnContours.h" #include "Minuit2/MnTraceObject.h" #include "Minuit2/MinimumBuilder.h" #include #include #include #include #ifdef USE_ROOT_ERROR #include "TROOT.h" #include "TMinuit2TraceObject.h" #endif namespace ROOT { namespace Minuit2 { // functions needed to control siwthc off of Minuit2 printing level #ifdef USE_ROOT_ERROR int TurnOffPrintInfoLevel() { // switch off Minuit2 printing of INFO message (cut off is 1001) int prevErrorIgnoreLevel = gErrorIgnoreLevel; if (prevErrorIgnoreLevel < 1001) { gErrorIgnoreLevel = 1001; return prevErrorIgnoreLevel; } return -2; // no op in this case } void RestoreGlobalPrintLevel(int value) { gErrorIgnoreLevel = value; } #else // dummy functions int TurnOffPrintInfoLevel() { return -1; } int ControlPrintLevel() { return -1; } void RestoreGlobalPrintLevel(int) {} #endif Minuit2Minimizer::Minuit2Minimizer(ROOT::Minuit2::EMinimizerType type) : Minimizer(), fDim(0), fMinimizer(0), fMinuitFCN(0), fMinimum(0) { // Default constructor implementation depending on minimizer type SetMinimizerType(type); } Minuit2Minimizer::Minuit2Minimizer(const char *type) : Minimizer(), fDim(0), fMinimizer(0), fMinuitFCN(0), fMinimum(0) { // constructor from a string std::string algoname(type); // tolower() is not an std function (Windows) std::transform(algoname.begin(), algoname.end(), algoname.begin(), (int (*)(int))tolower); EMinimizerType algoType = kMigrad; if (algoname == "simplex") algoType = kSimplex; if (algoname == "minimize") algoType = kCombined; if (algoname == "scan") algoType = kScan; if (algoname == "fumili") algoType = kFumili; if (algoname == "bfgs") algoType = kMigradBFGS; SetMinimizerType(algoType); } void Minuit2Minimizer::SetMinimizerType(ROOT::Minuit2::EMinimizerType type) { // Set minimizer algorithm type fUseFumili = false; switch (type) { case ROOT::Minuit2::kMigrad: // std::cout << "Minuit2Minimizer: minimize using MIGRAD " << std::endl; SetMinimizer(new ROOT::Minuit2::VariableMetricMinimizer()); return; case ROOT::Minuit2::kMigradBFGS: // std::cout << "Minuit2Minimizer: minimize using MIGRAD " << std::endl; SetMinimizer(new ROOT::Minuit2::VariableMetricMinimizer(VariableMetricMinimizer::BFGSType())); return; case ROOT::Minuit2::kSimplex: // std::cout << "Minuit2Minimizer: minimize using SIMPLEX " << std::endl; SetMinimizer(new ROOT::Minuit2::SimplexMinimizer()); return; case ROOT::Minuit2::kCombined: SetMinimizer(new ROOT::Minuit2::CombinedMinimizer()); return; case ROOT::Minuit2::kScan: SetMinimizer(new ROOT::Minuit2::ScanMinimizer()); return; case ROOT::Minuit2::kFumili: SetMinimizer(new ROOT::Minuit2::FumiliMinimizer()); fUseFumili = true; return; default: // migrad minimizer SetMinimizer(new ROOT::Minuit2::VariableMetricMinimizer()); } } Minuit2Minimizer::~Minuit2Minimizer() { // Destructor implementation. if (fMinimizer) delete fMinimizer; if (fMinuitFCN) delete fMinuitFCN; if (fMinimum) delete fMinimum; } Minuit2Minimizer::Minuit2Minimizer(const Minuit2Minimizer &) : ROOT::Math::Minimizer() { // Implementation of copy constructor. } Minuit2Minimizer &Minuit2Minimizer::operator=(const Minuit2Minimizer &rhs) { // Implementation of assignment operator. if (this == &rhs) return *this; // time saving self-test return *this; } void Minuit2Minimizer::Clear() { // delete the state in case of consecutive minimizations fState = MnUserParameterState(); // clear also the function minimum if (fMinimum) delete fMinimum; fMinimum = 0; } // set variables bool Minuit2Minimizer::SetVariable(unsigned int ivar, const std::string &name, double val, double step) { // set a free variable. // Add the variable if not existing otherwise set value if exists already // this is implemented in MnUserParameterState::Add // if index is wrong (i.e. variable already exists but with a different index return false) but // value is set for corresponding variable name // std::cout << " add parameter " << name << " " << val << " step " << step << std::endl; MnPrint print("Minuit2Minimizer::SetVariable", PrintLevel()); if (step <= 0) { print.Info("Parameter", name, "has zero or invalid step size - consider it as constant"); fState.Add(name.c_str(), val); } else fState.Add(name.c_str(), val, step); unsigned int minuit2Index = fState.Index(name.c_str()); if (minuit2Index != ivar) { print.Warn("Wrong index", minuit2Index, "used for the variable", name); ivar = minuit2Index; return false; } fState.RemoveLimits(ivar); return true; } bool Minuit2Minimizer::SetLowerLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double lower) { // add a lower bounded variable if (!SetVariable(ivar, name, val, step)) return false; fState.SetLowerLimit(ivar, lower); return true; } bool Minuit2Minimizer::SetUpperLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double upper) { // add a upper bounded variable if (!SetVariable(ivar, name, val, step)) return false; fState.SetUpperLimit(ivar, upper); return true; } bool Minuit2Minimizer::SetLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double lower, double upper) { // add a double bound variable if (!SetVariable(ivar, name, val, step)) return false; fState.SetLimits(ivar, lower, upper); return true; } bool Minuit2Minimizer::SetFixedVariable(unsigned int ivar, const std::string &name, double val) { // add a fixed variable // need a step size otherwise treated as a constant // use 10% double step = (val != 0) ? 0.1 * std::abs(val) : 0.1; if (!SetVariable(ivar, name, val, step)) { ivar = fState.Index(name.c_str()); } fState.Fix(ivar); return true; } std::string Minuit2Minimizer::VariableName(unsigned int ivar) const { // return the variable name if (ivar >= fState.MinuitParameters().size()) return std::string(); return fState.GetName(ivar); } int Minuit2Minimizer::VariableIndex(const std::string &name) const { // return the variable index // check if variable exist return fState.Trafo().FindIndex(name); } bool Minuit2Minimizer::SetVariableValue(unsigned int ivar, double val) { // set value for variable ivar (only for existing parameters) if (ivar >= fState.MinuitParameters().size()) return false; fState.SetValue(ivar, val); return true; } bool Minuit2Minimizer::SetVariableValues(const double *x) { // set value for variable ivar (only for existing parameters) unsigned int n = fState.MinuitParameters().size(); if (n == 0) return false; for (unsigned int ivar = 0; ivar < n; ++ivar) fState.SetValue(ivar, x[ivar]); return true; } bool Minuit2Minimizer::SetVariableStepSize(unsigned int ivar, double step) { // set the step-size of an existing variable // parameter must exist or return false if (ivar >= fState.MinuitParameters().size()) return false; fState.SetError(ivar, step); return true; } bool Minuit2Minimizer::SetVariableLowerLimit(unsigned int ivar, double lower) { // set the limits of an existing variable // parameter must exist or return false if (ivar >= fState.MinuitParameters().size()) return false; fState.SetLowerLimit(ivar, lower); return true; } bool Minuit2Minimizer::SetVariableUpperLimit(unsigned int ivar, double upper) { // set the limits of an existing variable // parameter must exist or return false if (ivar >= fState.MinuitParameters().size()) return false; fState.SetUpperLimit(ivar, upper); return true; } bool Minuit2Minimizer::SetVariableLimits(unsigned int ivar, double lower, double upper) { // set the limits of an existing variable // parameter must exist or return false if (ivar >= fState.MinuitParameters().size()) return false; fState.SetLimits(ivar, lower, upper); return true; } bool Minuit2Minimizer::FixVariable(unsigned int ivar) { // Fix an existing variable if (ivar >= fState.MinuitParameters().size()) return false; fState.Fix(ivar); return true; } bool Minuit2Minimizer::ReleaseVariable(unsigned int ivar) { // Release an existing variable if (ivar >= fState.MinuitParameters().size()) return false; fState.Release(ivar); return true; } bool Minuit2Minimizer::IsFixedVariable(unsigned int ivar) const { // query if variable is fixed if (ivar >= fState.MinuitParameters().size()) { MnPrint print("Minuit2Minimizer", PrintLevel()); print.Error("Wrong variable index"); return false; } return (fState.Parameter(ivar).IsFixed() || fState.Parameter(ivar).IsConst()); } bool Minuit2Minimizer::GetVariableSettings(unsigned int ivar, ROOT::Fit::ParameterSettings &varObj) const { // retrieve variable settings (all set info on the variable) if (ivar >= fState.MinuitParameters().size()) { MnPrint print("Minuit2Minimizer", PrintLevel()); print.Error("Wrong variable index"); return false; } const MinuitParameter &par = fState.Parameter(ivar); varObj.Set(par.Name(), par.Value(), par.Error()); if (par.HasLowerLimit()) { if (par.HasUpperLimit()) { varObj.SetLimits(par.LowerLimit(), par.UpperLimit()); } else { varObj.SetLowerLimit(par.LowerLimit()); } } else if (par.HasUpperLimit()) { varObj.SetUpperLimit(par.UpperLimit()); } if (par.IsConst() || par.IsFixed()) varObj.Fix(); return true; } void Minuit2Minimizer::SetFunction(const ROOT::Math::IMultiGenFunction &func) { // set function to be minimized if (fMinuitFCN) delete fMinuitFCN; fDim = func.NDim(); if (!fUseFumili) { fMinuitFCN = new ROOT::Minuit2::FCNAdapter(func, ErrorDef()); } else { // for Fumili the fit method function interface is required const ROOT::Math::FitMethodFunction *fcnfunc = dynamic_cast(&func); if (!fcnfunc) { MnPrint print("Minuit2Minimizer", PrintLevel()); print.Error("Wrong Fit method function for Fumili"); return; } fMinuitFCN = new ROOT::Minuit2::FumiliFCNAdapter(*fcnfunc, fDim, ErrorDef()); } } void Minuit2Minimizer::SetFunction(const ROOT::Math::IMultiGradFunction &func) { // set function to be minimized fDim = func.NDim(); if (fMinuitFCN) delete fMinuitFCN; if (!fUseFumili) { fMinuitFCN = new ROOT::Minuit2::FCNGradAdapter(func, ErrorDef()); } else { // for Fumili the fit method function interface is required const ROOT::Math::FitMethodGradFunction *fcnfunc = dynamic_cast(&func); if (!fcnfunc) { MnPrint print("Minuit2Minimizer", PrintLevel()); print.Error("Wrong Fit method function for Fumili"); return; } fMinuitFCN = new ROOT::Minuit2::FumiliFCNAdapter(*fcnfunc, fDim, ErrorDef()); } } bool Minuit2Minimizer::Minimize() { // perform the minimization // store a copy of FunctionMinimum MnPrint print("Minuit2Minimizer::Minimize", PrintLevel()); if (!fMinuitFCN) { print.Error("FCN function has not been set"); return false; } assert(GetMinimizer() != 0); // delete result of previous minimization if (fMinimum) delete fMinimum; fMinimum = 0; const int maxfcn = MaxFunctionCalls(); const double tol = Tolerance(); const int strategyLevel = Strategy(); fMinuitFCN->SetErrorDef(ErrorDef()); const int printLevel = PrintLevel(); if (PrintLevel() >= 1) { // print the real number of maxfcn used (defined in ModularFuncitonMinimizer) int maxfcn_used = maxfcn; if (maxfcn_used == 0) { int nvar = fState.VariableParameters(); maxfcn_used = 200 + 100 * nvar + 5 * nvar * nvar; } std::cout << "Minuit2Minimizer: Minimize with max-calls " << maxfcn_used << " convergence for edm < " << tol << " strategy " << strategyLevel << std::endl; } // internal minuit messages fMinimizer->Builder().SetPrintLevel(printLevel); // switch off Minuit2 printing const int prev_level = (printLevel <= 0) ? TurnOffPrintInfoLevel() : -2; const int prevGlobalLevel = MnPrint::SetGlobalLevel(printLevel); // set the precision if needed if (Precision() > 0) fState.SetPrecision(Precision()); // set strategy and add extra options if needed ROOT::Minuit2::MnStrategy strategy(strategyLevel); ROOT::Math::IOptions *minuit2Opt = ROOT::Math::MinimizerOptions::FindDefault("Minuit2"); if (minuit2Opt) { // set extra options int nGradCycles = strategy.GradientNCycles(); int nHessCycles = strategy.HessianNCycles(); int nHessGradCycles = strategy.HessianGradientNCycles(); double gradTol = strategy.GradientTolerance(); double gradStepTol = strategy.GradientStepTolerance(); double hessStepTol = strategy.HessianStepTolerance(); double hessG2Tol = strategy.HessianG2Tolerance(); minuit2Opt->GetValue("GradientNCycles", nGradCycles); minuit2Opt->GetValue("HessianNCycles", nHessCycles); minuit2Opt->GetValue("HessianGradientNCycles", nHessGradCycles); minuit2Opt->GetValue("GradientTolerance", gradTol); minuit2Opt->GetValue("GradientStepTolerance", gradStepTol); minuit2Opt->GetValue("HessianStepTolerance", hessStepTol); minuit2Opt->GetValue("HessianG2Tolerance", hessG2Tol); strategy.SetGradientNCycles(nGradCycles); strategy.SetHessianNCycles(nHessCycles); strategy.SetHessianGradientNCycles(nHessGradCycles); strategy.SetGradientTolerance(gradTol); strategy.SetGradientStepTolerance(gradStepTol); strategy.SetHessianStepTolerance(hessStepTol); strategy.SetHessianG2Tolerance(hessStepTol); int storageLevel = 1; bool ret = minuit2Opt->GetValue("StorageLevel", storageLevel); if (ret) SetStorageLevel(storageLevel); if (printLevel > 0) { std::cout << "Minuit2Minimizer::Minuit - Changing default options" << std::endl; minuit2Opt->Print(); } } // set a minimizer tracer object (default for printlevel=10, from gROOT for printLevel=11) // use some special print levels MnTraceObject *traceObj = 0; #ifdef USE_ROOT_ERROR if (printLevel == 10 && gROOT) { TObject *obj = gROOT->FindObject("Minuit2TraceObject"); traceObj = dynamic_cast(obj); if (traceObj) { // need to remove from the list gROOT->Remove(obj); } } if (printLevel == 20 || printLevel == 30 || printLevel == 40 || (printLevel >= 20000 && printLevel < 30000)) { int parNumber = printLevel - 20000; if (printLevel == 20) parNumber = -1; if (printLevel == 30) parNumber = -2; if (printLevel == 40) parNumber = 0; traceObj = new TMinuit2TraceObject(parNumber); } #endif if (printLevel == 100 || (printLevel >= 10000 && printLevel < 20000)) { int parNumber = printLevel - 10000; traceObj = new MnTraceObject(parNumber); } if (traceObj) { traceObj->Init(fState); SetTraceObject(*traceObj); } const ROOT::Minuit2::FCNGradientBase *gradFCN = dynamic_cast(fMinuitFCN); if (gradFCN != 0) { // use gradient // SetPrintLevel(3); ROOT::Minuit2::FunctionMinimum min = GetMinimizer()->Minimize(*gradFCN, fState, strategy, maxfcn, tol); fMinimum = new ROOT::Minuit2::FunctionMinimum(min); } else { ROOT::Minuit2::FunctionMinimum min = GetMinimizer()->Minimize(*GetFCN(), fState, strategy, maxfcn, tol); fMinimum = new ROOT::Minuit2::FunctionMinimum(min); } // check if Hesse needs to be run if (fMinimum->IsValid() && IsValidError() && fMinimum->State().Error().Dcovar() != 0) { // run Hesse (Hesse will add results in the last state of fMinimum ROOT::Minuit2::MnHesse hesse(strategy); hesse(*fMinuitFCN, *fMinimum, maxfcn); } // -2 is the highest low invalid value for gErrorIgnoreLevel if (prev_level > -2) RestoreGlobalPrintLevel(prev_level); MnPrint::SetGlobalLevel(prevGlobalLevel); // copy minimum state (parameter values and errors) fState = fMinimum->UserState(); bool ok = ExamineMinimum(*fMinimum); // fMinimum = 0; // delete trace object if it was constructed if (traceObj) { delete traceObj; } return ok; } bool Minuit2Minimizer::ExamineMinimum(const ROOT::Minuit2::FunctionMinimum &min) { /// study the function minimum // debug ( print all the states) int debugLevel = PrintLevel(); if (debugLevel >= 3) { const std::vector &iterationStates = min.States(); std::cout << "Number of iterations " << iterationStates.size() << std::endl; for (unsigned int i = 0; i < iterationStates.size(); ++i) { // std::cout << iterationStates[i] << std::endl; const ROOT::Minuit2::MinimumState &st = iterationStates[i]; std::cout << "----------> Iteration " << i << std::endl; int pr = std::cout.precision(12); std::cout << " FVAL = " << st.Fval() << " Edm = " << st.Edm() << " Nfcn = " << st.NFcn() << std::endl; std::cout.precision(pr); if (st.HasCovariance()) std::cout << " Error matrix change = " << st.Error().Dcovar() << std::endl; if (st.HasParameters()) { std::cout << " Parameters : "; // need to transform from internal to external for (int j = 0; j < st.size(); ++j) std::cout << " p" << j << " = " << fState.Int2ext(j, st.Vec()(j)); std::cout << std::endl; } } } fStatus = 0; std::string txt; if (!min.HasPosDefCovar()) { // this happens normally when Hesse failed // it can happen in case MnSeed failed (see ROOT-9522) txt = "Covar is not pos def"; fStatus = 5; } if (min.HasMadePosDefCovar()) { txt = "Covar was made pos def"; fStatus = 1; } if (min.HesseFailed()) { txt = "Hesse is not valid"; fStatus = 2; } if (min.IsAboveMaxEdm()) { txt = "Edm is above max"; fStatus = 3; } if (min.HasReachedCallLimit()) { txt = "Reached call limit"; fStatus = 4; } MnPrint print("Minuit2Minimizer::Minimize", debugLevel); bool validMinimum = min.IsValid(); if (validMinimum) { // print a warning message in case something is not ok if (fStatus != 0 && debugLevel > 0) print.Warn(txt); } else { // minimum is not valid when state is not valid and edm is over max or has passed call limits if (fStatus == 0) { // this should not happen txt = "unknown failure"; fStatus = 6; } print.Warn("Minimization did NOT converge,", txt); } if (debugLevel >= 1) PrintResults(); // set the minimum values in the fValues vector const std::vector ¶msObj = fState.MinuitParameters(); if (paramsObj.size() == 0) return 0; assert(fDim == paramsObj.size()); // re-size vector if it has changed after a new minimization if (fValues.size() != fDim) fValues.resize(fDim); for (unsigned int i = 0; i < fDim; ++i) { fValues[i] = paramsObj[i].Value(); } return validMinimum; } void Minuit2Minimizer::PrintResults() { // print results of minimization if (!fMinimum) return; if (fMinimum->IsValid()) { // valid minimum std::cout << "Minuit2Minimizer : Valid minimum - status = " << fStatus << std::endl; int pr = std::cout.precision(18); std::cout << "FVAL = " << fState.Fval() << std::endl; std::cout << "Edm = " << fState.Edm() << std::endl; std::cout.precision(pr); std::cout << "Nfcn = " << fState.NFcn() << std::endl; for (unsigned int i = 0; i < fState.MinuitParameters().size(); ++i) { const MinuitParameter &par = fState.Parameter(i); std::cout << par.Name() << "\t = " << par.Value() << "\t "; if (par.IsFixed()) std::cout << "(fixed)" << std::endl; else if (par.IsConst()) std::cout << "(const)" << std::endl; else if (par.HasLimits()) std::cout << "+/- " << par.Error() << "\t(limited)" << std::endl; else std::cout << "+/- " << par.Error() << std::endl; } } else { std::cout << "Minuit2Minimizer : Invalid Minimum - status = " << fStatus << std::endl; std::cout << "FVAL = " << fState.Fval() << std::endl; std::cout << "Edm = " << fState.Edm() << std::endl; std::cout << "Nfcn = " << fState.NFcn() << std::endl; } } const double *Minuit2Minimizer::Errors() const { // return error at minimum (set to zero for fixed and constant params) const std::vector ¶msObj = fState.MinuitParameters(); if (paramsObj.size() == 0) return 0; assert(fDim == paramsObj.size()); // be careful for multiple calls of this function. I will redo an allocation here // only when size of vectors has changed (e.g. after a new minimization) if (fErrors.size() != fDim) fErrors.resize(fDim); for (unsigned int i = 0; i < fDim; ++i) { const MinuitParameter &par = paramsObj[i]; if (par.IsFixed() || par.IsConst()) fErrors[i] = 0; else fErrors[i] = par.Error(); } return &fErrors.front(); } double Minuit2Minimizer::CovMatrix(unsigned int i, unsigned int j) const { // get value of covariance matrices (transform from external to internal indices) if (i >= fDim || j >= fDim) return 0; if (!fState.HasCovariance()) return 0; // no info available when minimization has failed if (fState.Parameter(i).IsFixed() || fState.Parameter(i).IsConst()) return 0; if (fState.Parameter(j).IsFixed() || fState.Parameter(j).IsConst()) return 0; unsigned int k = fState.IntOfExt(i); unsigned int l = fState.IntOfExt(j); return fState.Covariance()(k, l); } bool Minuit2Minimizer::GetCovMatrix(double *cov) const { // get value of covariance matrices if (!fState.HasCovariance()) return false; // no info available when minimization has failed for (unsigned int i = 0; i < fDim; ++i) { if (fState.Parameter(i).IsFixed() || fState.Parameter(i).IsConst()) { for (unsigned int j = 0; j < fDim; ++j) { cov[i * fDim + j] = 0; } } else { unsigned int l = fState.IntOfExt(i); for (unsigned int j = 0; j < fDim; ++j) { // could probably speed up this loop (if needed) int k = i * fDim + j; if (fState.Parameter(j).IsFixed() || fState.Parameter(j).IsConst()) cov[k] = 0; else { // need to transform from external to internal indices) // for taking care of the removed fixed row/columns in the Minuit2 representation unsigned int m = fState.IntOfExt(j); cov[k] = fState.Covariance()(l, m); } } } } return true; } bool Minuit2Minimizer::GetHessianMatrix(double *hess) const { // get value of Hessian matrix // this is the second derivative matrices if (!fState.HasCovariance()) return false; // no info available when minimization has failed for (unsigned int i = 0; i < fDim; ++i) { if (fState.Parameter(i).IsFixed() || fState.Parameter(i).IsConst()) { for (unsigned int j = 0; j < fDim; ++j) { hess[i * fDim + j] = 0; } } else { unsigned int l = fState.IntOfExt(i); for (unsigned int j = 0; j < fDim; ++j) { // could probably speed up this loop (if needed) int k = i * fDim + j; if (fState.Parameter(j).IsFixed() || fState.Parameter(j).IsConst()) hess[k] = 0; else { // need to transform from external to internal indices) // for taking care of the removed fixed row/columns in the Minuit2 representation unsigned int m = fState.IntOfExt(j); hess[k] = fState.Hessian()(l, m); } } } } return true; } double Minuit2Minimizer::Correlation(unsigned int i, unsigned int j) const { // get correlation between parameter i and j if (i >= fDim || j >= fDim) return 0; if (!fState.HasCovariance()) return 0; // no info available when minimization has failed if (fState.Parameter(i).IsFixed() || fState.Parameter(i).IsConst()) return 0; if (fState.Parameter(j).IsFixed() || fState.Parameter(j).IsConst()) return 0; unsigned int k = fState.IntOfExt(i); unsigned int l = fState.IntOfExt(j); double cij = fState.IntCovariance()(k, l); double tmp = std::sqrt(std::abs(fState.IntCovariance()(k, k) * fState.IntCovariance()(l, l))); if (tmp > 0) return cij / tmp; return 0; } double Minuit2Minimizer::GlobalCC(unsigned int i) const { // get global correlation coefficient for the parameter i. This is a number between zero and one which gives // the correlation between the i-th parameter and that linear combination of all other parameters which // is most strongly correlated with i. if (i >= fDim) return 0; // no info available when minimization has failed or has some problems if (!fState.HasGlobalCC()) return 0; if (fState.Parameter(i).IsFixed() || fState.Parameter(i).IsConst()) return 0; unsigned int k = fState.IntOfExt(i); return fState.GlobalCC().GlobalCC()[k]; } bool Minuit2Minimizer::GetMinosError(unsigned int i, double &errLow, double &errUp, int runopt) { // return the minos error for parameter i // if a minimum does not exist an error is returned // runopt is a flag which specifies if only lower or upper error needs to be run // if runopt = 0 both, = 1 only lower, + 2 only upper errors errLow = 0; errUp = 0; assert(fMinuitFCN); // need to know if parameter is const or fixed if (fState.Parameter(i).IsConst() || fState.Parameter(i).IsFixed()) { return false; } MnPrint print("Minuit2Minimizer::GetMinosError", PrintLevel()); // to run minos I need function minimum class // redo minimization from current state // ROOT::Minuit2::FunctionMinimum min = // GetMinimizer()->Minimize(*GetFCN(),fState, ROOT::Minuit2::MnStrategy(strategy), MaxFunctionCalls(), // Tolerance()); // fState = min.UserState(); if (fMinimum == 0) { print.Error("Failed - no function minimum existing"); return false; } if (!fMinimum->IsValid()) { print.Error("Failed - invalid function minimum"); return false; } fMinuitFCN->SetErrorDef(ErrorDef()); // if error def has been changed update it in FunctionMinimum if (ErrorDef() != fMinimum->Up()) fMinimum->SetErrorDef(ErrorDef()); int mstatus = RunMinosError(i, errLow, errUp, runopt); // run again the Minimization in case of a new minimum // bit 8 is set if ((mstatus & 8) != 0) { print.Info([&](std::ostream &os) { os << "Found a new minimum: run again the Minimization starting from the new point"; os << "\nFVAL = " << fState.Fval(); for (auto &par : fState.MinuitParameters()) { os << '\n' << par.Name() << "\t = " << par.Value(); } }); // release parameter that was fixed in the returned state from Minos ReleaseVariable(i); bool ok = Minimize(); if (!ok) return false; // run again Minos from new Minimum (also lower error needs to be re-computed) print.Info("Run now again Minos from the new found Minimum"); mstatus = RunMinosError(i, errLow, errUp, runopt); // do not reset new minimum bit to flag for other parameters mstatus |= 8; } fStatus += 10 * mstatus; fMinosStatus = mstatus; bool isValid = ((mstatus & 1) == 0) && ((mstatus & 2) == 0); return isValid; } int Minuit2Minimizer::RunMinosError(unsigned int i, double &errLow, double &errUp, int runopt) { bool runLower = runopt != 2; bool runUpper = runopt != 1; const int debugLevel = PrintLevel(); // switch off Minuit2 printing const int prev_level = (debugLevel <= 0) ? TurnOffPrintInfoLevel() : -2; const int prevGlobalLevel = MnPrint::SetGlobalLevel(debugLevel); // set the precision if needed if (Precision() > 0) fState.SetPrecision(Precision()); ROOT::Minuit2::MnMinos minos(*fMinuitFCN, *fMinimum); // run MnCross MnCross low; MnCross up; int maxfcn = MaxFunctionCalls(); double tol = Tolerance(); const char *par_name = fState.Name(i); // now input tolerance for migrad calls inside Minos (MnFunctionCross) // before it was fixed to 0.05 // cut off too small tolerance (they are not needed) tol = std::max(tol, 0.01); // get the real number of maxfcn used (defined in MnMinos) to be printed int maxfcn_used = maxfcn; if (maxfcn_used == 0) { int nvar = fState.VariableParameters(); maxfcn_used = 2 * (nvar + 1) * (200 + 100 * nvar + 5 * nvar * nvar); } if (runLower) { if (debugLevel >= 1) { std::cout << "************************************************************************************************" "******\n"; std::cout << "Minuit2Minimizer::GetMinosError - Run MINOS LOWER error for parameter #" << i << " : " << par_name << " using max-calls " << maxfcn_used << ", tolerance " << tol << std::endl; } low = minos.Loval(i, maxfcn, tol); } if (runUpper) { if (debugLevel >= 1) { std::cout << "************************************************************************************************" "******\n"; std::cout << "Minuit2Minimizer::GetMinosError - Run MINOS UPPER error for parameter #" << i << " : " << par_name << " using max-calls " << maxfcn_used << ", tolerance " << tol << std::endl; } up = minos.Upval(i, maxfcn, tol); } ROOT::Minuit2::MinosError me(i, fMinimum->UserState().Value(i), low, up); // restore global print level if (prev_level > -2) RestoreGlobalPrintLevel(prev_level); MnPrint::SetGlobalLevel(prevGlobalLevel); // debug result of Minos // print error message in Minos // Note that the only invalid condition can happen when the (npar-1) minimization fails // The error is also invalid when the maximum number of calls is reached or a new function minimum is found // in case of the parameter at the limit the error is not invalid. // When the error is invalid the returned error is the Hessian error. if (debugLevel > 0) { if (runLower) { if (!me.LowerValid()) std::cout << "Minos: Invalid lower error for parameter " << par_name << std::endl; if (me.AtLowerLimit()) std::cout << "Minos: Parameter : " << par_name << " is at Lower limit; error is " << me.Lower() << std::endl; if (me.AtLowerMaxFcn()) std::cout << "Minos: Maximum number of function calls exceeded when running for lower error for parameter " << par_name << std::endl; if (me.LowerNewMin()) std::cout << "Minos: New Minimum found while running Minos for lower error for parameter " << par_name << std::endl; if (debugLevel >= 1 && me.LowerValid()) std::cout << "Minos: Lower error for parameter " << par_name << " : " << me.Lower() << std::endl; } if (runUpper) { if (!me.UpperValid()) std::cout << "Minos: Invalid upper error for parameter " << par_name << std::endl; if (me.AtUpperLimit()) std::cout << "Minos: Parameter " << par_name << " is at Upper limit; error is " << me.Upper() << std::endl; if (me.AtUpperMaxFcn()) std::cout << "Minos: Maximum number of function calls exceeded when running for upper error for parameter " << par_name << std::endl; if (me.UpperNewMin()) std::cout << "Minos: New Minimum found while running Minos for upper error for parameter " << par_name << std::endl; if (debugLevel >= 1 && me.UpperValid()) std::cout << "Minos: Upper error for parameter " << par_name << " : " << me.Upper() << std::endl; } } MnPrint print("RunMinosError", PrintLevel()); bool lowerInvalid = (runLower && !me.LowerValid()); bool upperInvalid = (runUpper && !me.UpperValid()); // print message in case of invalid error also in printLevel0 if (lowerInvalid) { print.Warn("Invalid lower error for parameter", fMinimum->UserState().Name(i)); } if (upperInvalid) { print.Warn("Invalid upper error for parameter", fMinimum->UserState().Name(i)); } // print also case it is lower/upper limit if (me.AtLowerLimit()) { print.Warn("Lower error for parameter", fMinimum->UserState().Name(i), "is at the Lower limit!"); } if (me.AtUpperLimit()) { print.Warn("Upper error for parameter", fMinimum->UserState().Name(i), "is at the Upper limit!"); } int mstatus = 0; if (lowerInvalid || upperInvalid) { // set status accroding to bit // bit 1: lower invalid Minos errors // bit 2: upper invalid Minos error // bit 3: invalid because max FCN // bit 4 : invalid because a new minimum has been found if (lowerInvalid) { mstatus |= 1; if (me.AtLowerMaxFcn()) mstatus |= 4; if (me.LowerNewMin()) mstatus |= 8; } if (upperInvalid) { mstatus |= 2; if (me.AtUpperMaxFcn()) mstatus |= 4; if (me.UpperNewMin()) mstatus |= 8; } } // case upper/lower limit if (me.AtUpperLimit() || me.AtLowerLimit()) mstatus |= 16; if (runLower) errLow = me.Lower(); if (runUpper) errUp = me.Upper(); // in case of new minimum found update also the minimum state if ((runLower && me.LowerNewMin()) && (runUpper && me.UpperNewMin())) { // take state with lower function value fState = (low.State().Fval() < up.State().Fval()) ? low.State() : up.State(); } else if (runLower && me.LowerNewMin()) { fState = low.State(); } else if (runUpper && me.UpperNewMin()) { fState = up.State(); } return mstatus; } bool Minuit2Minimizer::Scan(unsigned int ipar, unsigned int &nstep, double *x, double *y, double xmin, double xmax) { // scan a parameter (variable) around the minimum value // the parameters must have been set before // if xmin=0 && xmax == 0 by default scan around 2 sigma of the error // if the errors are also zero then scan from min and max of parameter range MnPrint print("Minuit2Minimizer::Scan", PrintLevel()); if (!fMinuitFCN) { print.Error("Function must be set before using Scan"); return false; } if (ipar > fState.MinuitParameters().size()) { print.Error("Invalid number; minimizer variables must be set before using Scan"); return false; } // switch off Minuit2 printing const int prev_level = (PrintLevel() <= 0) ? TurnOffPrintInfoLevel() : -2; const int prevGlobalLevel = MnPrint::SetGlobalLevel(PrintLevel()); // set the precision if needed if (Precision() > 0) fState.SetPrecision(Precision()); MnParameterScan scan(*fMinuitFCN, fState.Parameters()); double amin = scan.Fval(); // fcn value of the function before scan // first value is param value std::vector> result = scan(ipar, nstep - 1, xmin, xmax); // restore global print level if (prev_level > -2) RestoreGlobalPrintLevel(prev_level); MnPrint::SetGlobalLevel(prevGlobalLevel); if (result.size() != nstep) { print.Error("Invalid result from MnParameterScan"); return false; } // sort also the returned points in x std::sort(result.begin(), result.end()); for (unsigned int i = 0; i < nstep; ++i) { x[i] = result[i].first; y[i] = result[i].second; } // what to do if a new minimum has been found ? // use that as new minimum if (scan.Fval() < amin) { print.Info("A new minimum has been found"); fState.SetValue(ipar, scan.Parameters().Value(ipar)); } return true; } bool Minuit2Minimizer::Contour(unsigned int ipar, unsigned int jpar, unsigned int &npoints, double *x, double *y) { // contour plot for parameter i and j // need a valid FunctionMinimum otherwise exits MnPrint print("Minuit2Minimizer::Contour", PrintLevel()); if (fMinimum == 0) { print.Error("No function minimum existing; must minimize function before"); return false; } if (!fMinimum->IsValid()) { print.Error("Invalid function minimum"); return false; } assert(fMinuitFCN); fMinuitFCN->SetErrorDef(ErrorDef()); // if error def has been changed update it in FunctionMinimum if (ErrorDef() != fMinimum->Up()) { fMinimum->SetErrorDef(ErrorDef()); } print.Info("Computing contours -", ErrorDef()); // switch off Minuit2 printing (for level of 0,1) const int prev_level = (PrintLevel() <= 1) ? TurnOffPrintInfoLevel() : -2; const int prevGlobalLevel = MnPrint::SetGlobalLevel(PrintLevel() - 1); // set the precision if needed if (Precision() > 0) fState.SetPrecision(Precision()); // eventually one should specify tolerance in contours MnContours contour(*fMinuitFCN, *fMinimum, Strategy()); // restore global print level if (prev_level > -2) RestoreGlobalPrintLevel(prev_level); MnPrint::SetGlobalLevel(prevGlobalLevel); // compute the contour std::vector> result = contour(ipar, jpar, npoints); if (result.size() != npoints) { print.Error("Invalid result from MnContours"); return false; } for (unsigned int i = 0; i < npoints; ++i) { x[i] = result[i].first; y[i] = result[i].second; } return true; } bool Minuit2Minimizer::Hesse() { // find Hessian (full second derivative calculations) // the contained state will be updated with the Hessian result // in case a function minimum exists and is valid the result will be // appended in the function minimum MnPrint print("Minuit2Minimizer::Hesse", PrintLevel()); if (!fMinuitFCN) { print.Error("FCN function has not been set"); return false; } const int strategy = Strategy(); const int maxfcn = MaxFunctionCalls(); print.Info("Using max-calls", maxfcn); // switch off Minuit2 printing const int prev_level = (PrintLevel() <= 0) ? TurnOffPrintInfoLevel() : -2; const int prevGlobalLevel = MnPrint::SetGlobalLevel(PrintLevel()); // set the precision if needed if (Precision() > 0) fState.SetPrecision(Precision()); ROOT::Minuit2::MnHesse hesse(strategy); // case when function minimum exists if (fMinimum) { // if (PrintLevel() >= 3) { // std::cout << "Minuit2Minimizer::Hesse - State before running Hesse " << std::endl; // std::cout << fState << std::endl; // } // run hesse and function minimum will be updated with Hesse result hesse(*fMinuitFCN, *fMinimum, maxfcn); // update user state fState = fMinimum->UserState(); } else { // run Hesse on point stored in current state (independent of function minimum validity) // (x == 0) fState = hesse(*fMinuitFCN, fState, maxfcn); } // restore global print level if (prev_level > -2) RestoreGlobalPrintLevel(prev_level); MnPrint::SetGlobalLevel(prevGlobalLevel); if (PrintLevel() >= 3) { std::cout << "Minuit2Minimizer::Hesse - State returned from Hesse " << std::endl; std::cout << fState << std::endl; } int covStatus = fState.CovarianceStatus(); std::string covStatusType = "not valid"; if (covStatus == 1) covStatusType = "approximate"; if (covStatus == 2) covStatusType = "full but made positive defined"; if (covStatus == 3) covStatusType = "accurate"; if (!fState.HasCovariance()) { // if false means error is not valid and this is due to a failure in Hesse // update minimizer error status int hstatus = 4; // information on error state can be retrieved only if fMinimum is available if (fMinimum) { if (fMinimum->Error().HesseFailed()) hstatus = 1; if (fMinimum->Error().InvertFailed()) hstatus = 2; else if (!(fMinimum->Error().IsPosDef())) hstatus = 3; } print.Warn("Hesse failed - matrix is", covStatusType); print.Warn(hstatus); fStatus += 100 * hstatus; return false; } print.Info("Hesse is valid - matrix is", covStatusType); return true; } int Minuit2Minimizer::CovMatrixStatus() const { // return status of covariance matrix //-1 - not available (inversion failed or Hesse failed) // 0 - available but not positive defined // 1 - covariance only approximate // 2 full matrix but forced pos def // 3 full accurate matrix if (fMinimum) { // case a function minimum is available if (fMinimum->HasAccurateCovar()) return 3; else if (fMinimum->HasMadePosDefCovar()) return 2; else if (fMinimum->HasValidCovariance()) return 1; else if (fMinimum->HasCovariance()) return 0; return -1; } else { // case fMinimum is not available - use state information return fState.CovarianceStatus(); } return 0; } void Minuit2Minimizer::SetTraceObject(MnTraceObject &obj) { // set trace object if (!fMinimizer) return; fMinimizer->Builder().SetTraceObject(obj); } void Minuit2Minimizer::SetStorageLevel(int level) { // set storage level if (!fMinimizer) return; fMinimizer->Builder().SetStorageLevel(level); } } // end namespace Minuit2 } // end namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnApplication.cxx0000644000000000000000000001463714332717401021101 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnApplication.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/FCNGradientBase.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { // constructor from non-gradient functions MnApplication::MnApplication(const FCNBase &fcn, const MnUserParameterState &state, const MnStrategy &stra, unsigned int nfcn) : fFCN(fcn), fState(state), fStrategy(stra), fNumCall(nfcn), fUseGrad(false) { } // constructor from functions MnApplication::MnApplication(const FCNGradientBase &fcn, const MnUserParameterState &state, const MnStrategy &stra, unsigned int nfcn) : fFCN(fcn), fState(state), fStrategy(stra), fNumCall(nfcn), fUseGrad(true) { } FunctionMinimum MnApplication::operator()(unsigned int maxfcn, double toler) { // constructor from macfcn calls and tolerance MnPrint print("MnApplication"); assert(fState.IsValid()); unsigned int npar = VariableParameters(); // assert(npar > 0); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; const FCNBase &fcn = Fcnbase(); assert(!fUseGrad || dynamic_cast(&fcn) != nullptr); FunctionMinimum min = fUseGrad ? Minimizer().Minimize(static_cast(fcn), fState, fStrategy, maxfcn, toler) : Minimizer().Minimize(fcn, fState, fStrategy, maxfcn, toler); fNumCall += min.NFcn(); fState = min.UserState(); const std::vector &iterationStates = min.States(); print.Debug("State resulting from Migrad after", iterationStates.size(), "iterations:", fState); print.Debug([&](std::ostream &os) { for (unsigned int i = 0; i < iterationStates.size(); ++i) { // std::cout << iterationStates[i] << std::endl; const ROOT::Minuit2::MinimumState &st = iterationStates[i]; os << "\n----------> Iteration " << i << '\n'; int pr = os.precision(18); os << " FVAL = " << st.Fval() << " Edm = " << st.Edm() << " Nfcn = " << st.NFcn() << '\n'; os.precision(pr); os << " Error matrix change = " << st.Error().Dcovar() << '\n'; os << " Internal parameters : "; for (int j = 0; j < st.size(); ++j) os << " p" << j << " = " << st.Vec()(j); } }); return min; } // facade: forward interface of MnUserParameters and MnUserTransformation // via MnUserParameterState const std::vector &MnApplication::MinuitParameters() const { // access to parameters (row-wise) return fState.MinuitParameters(); } // access to parameters and errors in column-wise representation std::vector MnApplication::Params() const { return fState.Params(); } std::vector MnApplication::Errors() const { return fState.Errors(); } const MinuitParameter &MnApplication::Parameter(unsigned int i) const { // access to single Parameter return fState.Parameter(i); } void MnApplication::Add(const char *name, double val, double err) { // add free Parameter fState.Add(name, val, err); } void MnApplication::Add(const char *name, double val, double err, double low, double up) { // add limited Parameter fState.Add(name, val, err, low, up); } void MnApplication::Add(const char *name, double val) { // add const Parameter fState.Add(name, val); } // interaction via external number of Parameter void MnApplication::Fix(unsigned int i) { fState.Fix(i); } void MnApplication::Release(unsigned int i) { fState.Release(i); } void MnApplication::SetValue(unsigned int i, double val) { // set value for parameter i fState.SetValue(i, val); } void MnApplication::SetError(unsigned int i, double val) { // set parameter error fState.SetError(i, val); } void MnApplication::SetLimits(unsigned int i, double low, double up) { // set parameter limits fState.SetLimits(i, low, up); } void MnApplication::RemoveLimits(unsigned int i) { fState.RemoveLimits(i); } double MnApplication::Value(unsigned int i) const { return fState.Value(i); } double MnApplication::Error(unsigned int i) const { return fState.Error(i); } // interaction via Name of Parameter void MnApplication::Fix(const char *i) { fState.Fix(i); } void MnApplication::Release(const char *i) { fState.Release(i); } void MnApplication::SetValue(const char *i, double val) { fState.SetValue(i, val); } void MnApplication::SetError(const char *i, double val) { fState.SetError(i, val); } void MnApplication::SetLimits(const char *i, double low, double up) { fState.SetLimits(i, low, up); } void MnApplication::RemoveLimits(const char *i) { fState.RemoveLimits(i); } void MnApplication::SetPrecision(double eps) { fState.SetPrecision(eps); } double MnApplication::Value(const char *i) const { return fState.Value(i); } double MnApplication::Error(const char *i) const { return fState.Error(i); } unsigned int MnApplication::Index(const char *name) const { // convert name into external number of Parameter return fState.Index(name); } const char *MnApplication::Name(unsigned int i) const { // convert external number into name of Parameter return fState.Name(i); } double MnApplication::Int2ext(unsigned int i, double val) const { // transformation internal -> external return fState.Int2ext(i, val); } double MnApplication::Ext2int(unsigned int e, double val) const { // transformation external -> internal return fState.Ext2int(e, val); } unsigned int MnApplication::IntOfExt(unsigned int ext) const { // get internal index for external parameter with index ext return fState.IntOfExt(ext); } unsigned int MnApplication::ExtOfInt(unsigned int internal) const { // get external index for internal parameter with index internal return fState.ExtOfInt(internal); } unsigned int MnApplication::VariableParameters() const { // get number of variable parameters return fState.VariableParameters(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnContours.cxx0000644000000000000000000001621414332717401020443 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnContours.h" #include "Minuit2/MnMinos.h" #include "Minuit2/MnMigrad.h" #include "Minuit2/MnFunctionCross.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnCross.h" #include "Minuit2/MinosError.h" #include "Minuit2/ContoursError.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { std::vector> MnContours:: operator()(unsigned int px, unsigned int py, unsigned int npoints) const { // get contour as a pair of (x,y) points passing the parameter index (px, py) and the number of requested points // (>=4) ContoursError cont = Contour(px, py, npoints); return cont(); } ContoursError MnContours::Contour(unsigned int px, unsigned int py, unsigned int npoints) const { // calculate the contour passing the parameter index (px, py) and the number of requested points (>=4) // the fcn.UP() has to be set to the rquired value (see Minuit document on errors) assert(npoints > 3); unsigned int maxcalls = 100 * (npoints + 5) * (fMinimum.UserState().VariableParameters() + 1); unsigned int nfcn = 0; MnPrint print("MnContours"); std::vector> result; result.reserve(npoints); std::vector states; // double edmmax = 0.5*0.05*fFCN.Up()*1.e-3; // double toler = 0.05; double toler = 0.1; // use same defaut value as in Minos // get first four points // std::cout<<"MnContours: get first 4 params."< ex = mex(); MinosError mey = minos.Minos(py); nfcn += mey.NFcn(); if (!mey.IsValid()) { print.Error("unable to find second two points"); return ContoursError(px, py, result, mex, mey, nfcn); } std::pair ey = mey(); MnMigrad migrad(fFCN, fMinimum.UserState(), MnStrategy(std::max(0, int(fStrategy.Strategy() - 1)))); migrad.Fix(px); migrad.SetValue(px, valx + ex.second); FunctionMinimum exy_up = migrad(); nfcn += exy_up.NFcn(); if (!exy_up.IsValid()) { print.Error("unable to find Upper y Value for x Parameter", px); return ContoursError(px, py, result, mex, mey, nfcn); } migrad.SetValue(px, valx + ex.first); FunctionMinimum exy_lo = migrad(); nfcn += exy_lo.NFcn(); if (!exy_lo.IsValid()) { print.Error("unable to find Lower y Value for x Parameter", px); return ContoursError(px, py, result, mex, mey, nfcn); } MnMigrad migrad1(fFCN, fMinimum.UserState(), MnStrategy(std::max(0, int(fStrategy.Strategy() - 1)))); migrad1.Fix(py); migrad1.SetValue(py, valy + ey.second); FunctionMinimum eyx_up = migrad1(); nfcn += eyx_up.NFcn(); if (!eyx_up.IsValid()) { print.Error("unable to find Upper x Value for y Parameter", py); return ContoursError(px, py, result, mex, mey, nfcn); } migrad1.SetValue(py, valy + ey.first); FunctionMinimum eyx_lo = migrad1(); nfcn += eyx_lo.NFcn(); if (!eyx_lo.IsValid()) { print.Error("unable to find Lower x Value for y Parameter", py); return ContoursError(px, py, result, mex, mey, nfcn); } double scalx = 1. / (ex.second - ex.first); double scaly = 1. / (ey.second - ey.first); result.emplace_back(valx + ex.first, exy_lo.UserState().Value(py)); result.emplace_back(eyx_lo.UserState().Value(px), valy + ey.first); result.emplace_back(valx + ex.second, exy_up.UserState().Value(py)); result.emplace_back(eyx_up.UserState().Value(px), valy + ey.second); MnUserParameterState upar = fMinimum.UserState(); print.Info("List of found points", '\n', " Parameter x is", upar.Name(px), '\n', " Parameter y is", upar.Name(py), '\n', result[0], '\n', result[1], '\n', result[2], '\n', result[3]); upar.Fix(px); upar.Fix(py); std::vector par(2); par[0] = px; par[1] = py; MnFunctionCross cross(fFCN, upar, fMinimum.Fval(), fStrategy); for (unsigned int i = 4; i < npoints; i++) { auto idist1 = result.end() - 1; auto idist2 = result.begin(); double dx = idist1->first - (idist2)->first; double dy = idist1->second - (idist2)->second; double bigdis = scalx * scalx * dx * dx + scaly * scaly * dy * dy; for (auto ipair = result.begin(); ipair != result.end() - 1; ++ipair) { double distx = ipair->first - (ipair + 1)->first; double disty = ipair->second - (ipair + 1)->second; double dist = scalx * scalx * distx * distx + scaly * scaly * disty * disty; if (dist > bigdis) { bigdis = dist; idist1 = ipair; idist2 = ipair + 1; } } double a1 = 0.5; double a2 = 0.5; double sca = 1.; L300: if (nfcn > maxcalls) { print.Error("maximum number of function calls exhausted"); return ContoursError(px, py, result, mex, mey, nfcn); } double xmidcr = a1 * idist1->first + a2 * (idist2)->first; double ymidcr = a1 * idist1->second + a2 * (idist2)->second; double xdir = (idist2)->second - idist1->second; double ydir = idist1->first - (idist2)->first; double scalfac = sca * std::max(std::fabs(xdir * scalx), std::fabs(ydir * scaly)); double xdircr = xdir / scalfac; double ydircr = ydir / scalfac; std::vector pmid(2); pmid[0] = xmidcr; pmid[1] = ymidcr; std::vector pdir(2); pdir[0] = xdircr; pdir[1] = ydircr; MnCross opt = cross(par, pmid, pdir, toler, maxcalls); nfcn += opt.NFcn(); if (!opt.IsValid()) { // if(a1 > 0.5) { if (sca < 0.) { print.Error("unable to find point on Contour", i + 1, '\n', "found only", i, "points"); return ContoursError(px, py, result, mex, mey, nfcn); } // a1 = 0.75; // a2 = 0.25; // std::cout<<"*****switch direction"< 0); assert(n < cov.Nrow()); MnPrint print("MnCovarianceSqueeze"); MnAlgebraicSymMatrix hess(cov.Nrow()); for (unsigned int i = 0; i < cov.Nrow(); i++) { for (unsigned int j = i; j < cov.Nrow(); j++) { hess(i, j) = cov(i, j); } } int ifail = Invert(hess); if (ifail != 0) { print.Warn("inversion failed; return diagonal matrix;"); MnUserCovariance result(cov.Nrow() - 1); for (unsigned int i = 0, j = 0; i < cov.Nrow(); i++) { if (i == n) continue; result(j, j) = cov(i, i); j++; } return result; } MnAlgebraicSymMatrix squeezed = (*this)(hess, n); ifail = Invert(squeezed); if (ifail != 0) { print.Warn("back-inversion failed; return diagonal matrix;"); MnUserCovariance result(squeezed.Nrow()); for (unsigned int i = 0; i < squeezed.Nrow(); i++) { result(i, i) = 1. / squeezed(i, i); } return result; } return MnUserCovariance(std::vector(squeezed.Data(), squeezed.Data() + squeezed.size()), squeezed.Nrow()); } MinimumError MnCovarianceSqueeze::operator()(const MinimumError &err, unsigned int n) const { MnPrint print("MnCovarianceSqueeze"); // squueze the minimum error class // Remove index-row on the Hessian matrix and the get the new correct error matrix // (inverse of new Hessian) MnAlgebraicSymMatrix hess = err.Hessian(); MnAlgebraicSymMatrix squeezed = (*this)(hess, n); int ifail = Invert(squeezed); if (ifail != 0) { print.Warn("MinimumError inversion fails; return diagonal matrix."); MnAlgebraicSymMatrix tmp(squeezed.Nrow()); for (unsigned int i = 0; i < squeezed.Nrow(); i++) { tmp(i, i) = 1. / squeezed(i, i); } return MinimumError(tmp, MinimumError::MnInvertFailed); } return MinimumError(squeezed, err.Dcovar()); } MnAlgebraicSymMatrix MnCovarianceSqueeze::operator()(const MnAlgebraicSymMatrix &hess, unsigned int n) const { // squueze a symmetrix matrix (remove entire row and column n) assert(hess.Nrow() > 0); assert(n < hess.Nrow()); MnAlgebraicSymMatrix hs(hess.Nrow() - 1); for (unsigned int i = 0, j = 0; i < hess.Nrow(); i++) { if (i == n) continue; for (unsigned int k = i, l = j; k < hess.Nrow(); k++) { if (k == n) continue; hs(j, l) = hess(i, k); l++; } j++; } return hs; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnEigen.cxx0000644000000000000000000000220314332717401017647 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnEigen.h" #include "Minuit2/MnUserCovariance.h" #include "Minuit2/MnMatrix.h" namespace ROOT { namespace Minuit2 { LAVector eigenvalues(const LASymMatrix &); std::vector MnEigen::operator()(const MnUserCovariance &covar) const { // wrapper to calculate eigenvalues of the covariance matrix using mneigen function LASymMatrix cov(covar.Nrow()); for (unsigned int i = 0; i < covar.Nrow(); i++) for (unsigned int j = i; j < covar.Nrow(); j++) cov(i, j) = covar(i, j); LAVector eigen = eigenvalues(cov); std::vector result(eigen.Data(), eigen.Data() + covar.Nrow()); return result; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnFcn.cxx0000644000000000000000000000215014332717401017327 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnFcn.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnVectorTransform.h" namespace ROOT { namespace Minuit2 { MnFcn::~MnFcn() { // std::cout<<"Total number of calls to FCN: "<& par) const { // return fFCN(par); // } double MnFcn::ErrorDef() const { return fFCN.Up(); } double MnFcn::Up() const { return fFCN.Up(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnFumiliMinimize.cxx0000644000000000000000000000221114332717401021546 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnFumiliMinimize.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/FumiliMinimizer.h" namespace ROOT { namespace Minuit2 { FunctionMinimum MnFumiliMinimize::operator()(unsigned int maxfcn, double toler) { // minimize using Fumili // need to reimplement otherwise base class method is done assert(fState.IsValid()); unsigned int npar = VariableParameters(); // assert(npar > 0); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; FunctionMinimum min = Minimizer().Minimize(Fcnbase(), fState, fStrategy, maxfcn, toler); fNumCall += min.NFcn(); fState = min.UserState(); return min; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnFunctionCross.cxx0000644000000000000000000004144214332717401021427 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnFunctionCross.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnMigrad.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnParabola.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/MnParabolaFactory.h" #include "Minuit2/MnCross.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { MnCross MnFunctionCross::operator()(const std::vector &par, const std::vector &pmid, const std::vector &pdir, double tlr, unsigned int maxcalls) const { // evaluate crossing point where function is equal to MIN + UP, // with direction pdir from values pmid // tlr indicate tolerance and maxcalls maximum number of calls // double edmmax = 0.5*0.001*toler*fFCN.Up(); unsigned int npar = par.size(); unsigned int nfcn = 0; const MnMachinePrecision &prec = fState.Precision(); // tolerance used when calling Migrad double mgr_tlr = 0.5 * tlr; // to be consistent with F77 version (for default values of tlr which is 0.1) // other olerance values are fixed at 0.01 tlr = 0.01; // convergence when F is within tlf of aim and next prediction // of aopt is within tla of previous value of aopt double up = fFCN.Up(); // for finding the point : double tlf = tlr * up; double tla = tlr; unsigned int maxitr = 15; unsigned int ipt = 0; double aminsv = fFval; double aim = aminsv + up; // std::cout<<"aim= "< alsb(3, 0.), flsb(3, 0.); MnPrint print("MnFunctionCross"); print.Debug([&](std::ostream &os) { for (unsigned int i = 0; i < par.size(); ++i) os << "Parameter " << par[i] << " value " << pmid[i] << " dir " << pdir[i] << " function min = " << aminsv << " contur value aim = (fmin + up) = " << aim; }); // find the largest allowed aulim double aulim = 100.; for (unsigned int i = 0; i < par.size(); i++) { unsigned int kex = par[i]; if (fState.Parameter(kex).HasLimits()) { double zmid = pmid[i]; double zdir = pdir[i]; // double zlim = 0.; if (zdir > 0. && fState.Parameter(kex).HasUpperLimit()) { double zlim = fState.Parameter(kex).UpperLimit(); if (std::fabs(zdir) < fState.Precision().Eps()) { // we have a limit if (std::fabs(zlim - zmid) < fState.Precision().Eps()) limset = true; continue; } aulim = std::min(aulim, (zlim - zmid) / zdir); } else if (zdir < 0. && fState.Parameter(kex).HasLowerLimit()) { double zlim = fState.Parameter(kex).LowerLimit(); if (std::fabs(zdir) < fState.Precision().Eps()) { // we have a limit if (std::fabs(zlim - zmid) < fState.Precision().Eps()) limset = true; continue; } aulim = std::min(aulim, (zlim - zmid) / zdir); } } } print.Debug("Largest allowed aulim", aulim); // case of a single parameter and we are at limit if (limset && npar == 1) { print.Warn("Parameter is at limit", pmid[0], "delta", pdir[0]); return MnCross(fState, nfcn, MnCross::CrossParLimit()); } if (aulim < aopt + tla) limset = true; MnMigrad migrad(fFCN, fState, MnStrategy(std::max(0, int(fStrategy.Strategy() - 1)))); print.Info([&](std::ostream &os) { os << "Run Migrad with fixed parameters:"; for (unsigned i = 0; i < npar; ++i) os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i]; }); for (unsigned int i = 0; i < npar; i++) migrad.SetValue(par[i], pmid[i]); // find minimum with respect all the other parameters (n- npar) (npar are the fixed ones) FunctionMinimum min0 = migrad(maxcalls, mgr_tlr); nfcn += min0.NFcn(); print.Info("Result after Migrad", MnPrint::Oneline(min0), min0.UserState().Parameters()); // case a new minimum is found if (min0.Fval() < fFval - tlf) { // case of new minimum is found print.Warn("New minimum found while scanning parameter", par.front(), "new value =", min0.Fval(), "old value =", fFval); return MnCross(min0.UserState(), nfcn, MnCross::CrossNewMin()); } if (min0.HasReachedCallLimit()) return MnCross(min0.UserState(), nfcn, MnCross::CrossFcnLimit()); if (!min0.IsValid()) return MnCross(fState, nfcn); if (limset == true && min0.Fval() < aim) return MnCross(min0.UserState(), nfcn, MnCross::CrossParLimit()); ipt++; alsb[0] = 0.; flsb[0] = min0.Fval(); flsb[0] = std::max(flsb[0], aminsv + 0.1 * up); aopt = std::sqrt(up / (flsb[0] - aminsv)) - 1.; if (std::fabs(flsb[0] - aim) < tlf) return MnCross(aopt, min0.UserState(), nfcn); if (aopt > 1.) aopt = 1.; if (aopt < -0.5) aopt = -0.5; limset = false; if (aopt > aulim) { aopt = aulim; limset = true; } print.Debug("flsb[0]", flsb[0], "aopt", aopt); print.Info([&](std::ostream &os) { os << "Run Migrad again (2nd) with fixed parameters:"; for (unsigned i = 0; i < npar; ++i) os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i] + (aopt)*pdir[i]; }); for (unsigned int i = 0; i < npar; i++) migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]); FunctionMinimum min1 = migrad(maxcalls, mgr_tlr); nfcn += min1.NFcn(); print.Info("Result after 2nd Migrad", MnPrint::Oneline(min1), min1.UserState().Parameters()); if (min1.Fval() < fFval - tlf) // case of new minimum found return MnCross(min1.UserState(), nfcn, MnCross::CrossNewMin()); if (min1.HasReachedCallLimit()) return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit()); if (!min1.IsValid()) return MnCross(fState, nfcn); if (limset == true && min1.Fval() < aim) return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit()); ipt++; alsb[1] = aopt; flsb[1] = min1.Fval(); double dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0]); print.Debug("aopt", aopt, "min1Val", flsb[1], "dfda", dfda); L300: if (dfda < 0.) { // looking for slope of the right sign print.Debug("dfda < 0 - iterate from", ipt, "to max of", maxitr); // iterate (max times is maxitr) incrementing aopt unsigned int maxlk = maxitr - ipt; for (unsigned int it = 0; it < maxlk; it++) { alsb[0] = alsb[1]; flsb[0] = flsb[1]; // LM: Add + 1, looking at Fortran code it starts from 1 ( see bug #8396) aopt = alsb[0] + 0.2 * (it + 1); limset = false; if (aopt > aulim) { aopt = aulim; limset = true; } print.Info([&](std::ostream &os) { os << "Run Migrad again (iteration " << it << " ) :"; for (unsigned i = 0; i < npar; ++i) os << "\n parameter " << par[i] << " (" << fState.Name(par[i]) << ") fixed to " << pmid[i] + (aopt)*pdir[i]; }); for (unsigned int i = 0; i < npar; i++) migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]); min1 = migrad(maxcalls, mgr_tlr); nfcn += min1.NFcn(); print.Info("Result after Migrad", MnPrint::Oneline(min1), '\n', min1.UserState().Parameters()); if (min1.Fval() < fFval - tlf) // case of new minimum found return MnCross(min1.UserState(), nfcn, MnCross::CrossNewMin()); if (min1.HasReachedCallLimit()) return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit()); if (!min1.IsValid()) return MnCross(fState, nfcn); if (limset == true && min1.Fval() < aim) return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit()); ipt++; alsb[1] = aopt; flsb[1] = min1.Fval(); dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0]); // if(dfda > 0.) goto L460; print.Debug("aopt", aopt, "min1Val", flsb[1], "dfda", dfda); if (dfda > 0.) break; } if (ipt > maxitr) return MnCross(fState, nfcn); } // if(dfda < 0.) L460: // dfda > 0: we have two points with the right slope aopt = alsb[1] + (aim - flsb[1]) / dfda; print.Debug("dfda > 0 : aopt", aopt); double fdist = std::min(std::fabs(aim - flsb[0]), std::fabs(aim - flsb[1])); double adist = std::min(std::fabs(aopt - alsb[0]), std::fabs(aopt - alsb[1])); tla = tlr; if (std::fabs(aopt) > 1.) tla = tlr * std::fabs(aopt); if (adist < tla && fdist < tlf) return MnCross(aopt, min1.UserState(), nfcn); if (ipt > maxitr) return MnCross(fState, nfcn); double bmin = std::min(alsb[0], alsb[1]) - 1.; if (aopt < bmin) aopt = bmin; double bmax = std::max(alsb[0], alsb[1]) + 1.; if (aopt > bmax) aopt = bmax; limset = false; if (aopt > aulim) { aopt = aulim; limset = true; } print.Info([&](std::ostream &os) { os << "Run Migrad again (3rd) with fixed parameters:"; for (unsigned i = 0; i < npar; ++i) os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i] + (aopt)*pdir[i]; }); for (unsigned int i = 0; i < npar; i++) migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]); FunctionMinimum min2 = migrad(maxcalls, mgr_tlr); nfcn += min2.NFcn(); print.Info("Result after Migrad (3rd):", MnPrint::Oneline(min2), min2.UserState().Parameters()); if (min2.Fval() < fFval - tlf) // case of new minimum found return MnCross(min2.UserState(), nfcn, MnCross::CrossNewMin()); if (min2.HasReachedCallLimit()) return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit()); if (!min2.IsValid()) return MnCross(fState, nfcn); if (limset == true && min2.Fval() < aim) return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit()); ipt++; alsb[2] = aopt; flsb[2] = min2.Fval(); // now we have three points, ask how many < AIM double ecarmn = std::fabs(flsb[2] - aim); double ecarmx = 0.; unsigned int ibest = 2; unsigned int iworst = 0; unsigned int noless = 0; for (unsigned int i = 0; i < 3; i++) { double ecart = std::fabs(flsb[i] - aim); if (ecart > ecarmx) { ecarmx = ecart; iworst = i; } if (ecart < ecarmn) { ecarmn = ecart; ibest = i; } if (flsb[i] < aim) noless++; } print.Debug("have three points : noless < aim; noless", noless, "ibest", ibest, "iworst", iworst); // std::cout<<"480"< 0.) print.Warn("Problem 1"); // find with root is the right one aopt = x1; double slope = s1; if (s2 > 0.) { aopt = x2; slope = s2; } print.Debug("Parabola fit: aopt", aopt, "slope", slope); // ask if converged tla = tlr; if (std::fabs(aopt) > 1.) tla = tlr * std::fabs(aopt); print.Debug("Delta(aopt)", std::fabs(aopt - alsb[ibest]), "tla", tla, "Delta(F)", std::fabs(flsb[ibest] - aim), "tlf", tlf); if (std::fabs(aopt - alsb[ibest]) < tla && std::fabs(flsb[ibest] - aim) < tlf) return MnCross(aopt, min2.UserState(), nfcn); // if(ipt > maxitr) return MnCross(); // see if proposed point is in acceptable zone between L and R // first find ileft, iright, iout and ibest unsigned int ileft = 3; unsigned int iright = 3; unsigned int iout = 3; ibest = 0; ecarmx = 0.; ecarmn = std::fabs(aim - flsb[0]); for (unsigned int i = 0; i < 3; i++) { double ecart = std::fabs(flsb[i] - aim); if (ecart < ecarmn) { ecarmn = ecart; ibest = i; } if (ecart > ecarmx) ecarmx = ecart; if (flsb[i] > aim) { if (iright == 3) iright = i; else if (flsb[i] > flsb[iright]) iout = i; else { iout = iright; iright = i; } } else if (ileft == 3) ileft = i; else if (flsb[i] < flsb[ileft]) iout = i; else { iout = ileft; ileft = i; } } print.Debug("ileft", ileft, "iright", iright, "iout", iout, "ibest", ibest); // avoid keeping a bad point nest time around if (ecarmx > 10. * std::fabs(flsb[iout] - aim)) aopt = 0.5 * (aopt + 0.5 * (alsb[iright] + alsb[ileft])); // knowing ileft and iright, get acceptable window double smalla = 0.1 * tla; if (slope * smalla > tlf) smalla = tlf / slope; double aleft = alsb[ileft] + smalla; double aright = alsb[iright] - smalla; // move proposed point AOPT into window if necessary if (aopt < aleft) aopt = aleft; if (aopt > aright) aopt = aright; if (aleft > aright) aopt = 0.5 * (aleft + aright); // see if proposed point outside limits (should be impossible) limset = false; if (aopt > aulim) { aopt = aulim; limset = true; } // evaluate at new point aopt print.Info([&](std::ostream &os) { os << "Run Migrad again at new point (#iter = " << ipt+1 << " ):"; for (unsigned i = 0; i < npar; ++i) os << "\n\t - parameter " << par[i] << " fixed to " << pmid[i] + (aopt)*pdir[i]; }); for (unsigned int i = 0; i < npar; i++) migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]); min2 = migrad(maxcalls, mgr_tlr); nfcn += min2.NFcn(); print.Info("Result after new Migrad:", MnPrint::Oneline(min2), min2.UserState().Parameters()); if (min2.Fval() < fFval - tlf) // case of new minimum found return MnCross(min2.UserState(), nfcn, MnCross::CrossNewMin()); if (min2.HasReachedCallLimit()) return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit()); if (!min2.IsValid()) return MnCross(fState, nfcn); if (limset == true && min2.Fval() < aim) return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit()); ipt++; // replace odd point with new one (which is the best of three) alsb[iout] = aopt; flsb[iout] = min2.Fval(); ibest = iout; } while (ipt < maxitr); // goto L500; return MnCross(fState, nfcn); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnGlobalCorrelationCoeff.cxx0000644000000000000000000000254014332717401023171 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnGlobalCorrelationCoeff.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { MnGlobalCorrelationCoeff::MnGlobalCorrelationCoeff(const MnAlgebraicSymMatrix &cov) : fGlobalCC(std::vector()), fValid(true) { // constructor: calculate global correlation given a symmetric matrix MnPrint print("MnGlobalCorrelationCoeff"); MnAlgebraicSymMatrix inv(cov); int ifail = Invert(inv); if (ifail != 0) { print.Warn("inversion of matrix fails"); fValid = false; } else { unsigned int n = cov.Nrow(); fGlobalCC.reserve(n); for (unsigned int i = 0; i < n; i++) { double denom = inv(i, i) * cov(i, i); if (denom < 1. && denom > 0.) fGlobalCC.push_back(0.); else fGlobalCC.push_back(std::sqrt(1. - 1. / denom)); } } } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnHesse.cxx0000644000000000000000000003546514332717401017707 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnHesse.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnUserFcn.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnPosDef.h" #include "Minuit2/HessianGradientCalculator.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/MinimumState.h" #include "Minuit2/VariableMetricEDMEstimator.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnPrint.h" #include "Minuit2/MPIProcess.h" namespace ROOT { namespace Minuit2 { MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int maxcalls) const { // interface from vector of params and errors return (*this)(fcn, MnUserParameterState(par, err), maxcalls); } MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int maxcalls) const { // interface from vector of params and covariance return (*this)(fcn, MnUserParameterState(par, cov, nrow), maxcalls); } MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const std::vector &par, const MnUserCovariance &cov, unsigned int maxcalls) const { // interface from vector of params and covariance return (*this)(fcn, MnUserParameterState(par, cov), maxcalls); } MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const MnUserParameters &par, unsigned int maxcalls) const { // interface from MnUserParameters return (*this)(fcn, MnUserParameterState(par), maxcalls); } MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const MnUserParameters &par, const MnUserCovariance &cov, unsigned int maxcalls) const { // interface from MnUserParameters and MnUserCovariance return (*this)(fcn, MnUserParameterState(par, cov), maxcalls); } MnUserParameterState MnHesse::operator()(const FCNBase &fcn, const MnUserParameterState &state, unsigned int maxcalls) const { // interface from MnUserParameterState // create a new Minimum state and use that interface unsigned int n = state.VariableParameters(); MnUserFcn mfcn(fcn, state.Trafo(), state.NFcn()); MnAlgebraicVector x(n); for (unsigned int i = 0; i < n; i++) x(i) = state.IntParameters()[i]; double amin = mfcn(x); Numerical2PGradientCalculator gc(mfcn, state.Trafo(), fStrategy); MinimumParameters par(x, amin); FunctionGradient gra = gc(par); MinimumState tmp = (*this)(mfcn, MinimumState(par, MinimumError(MnAlgebraicSymMatrix(n), 1.), gra, state.Edm(), state.NFcn()), state.Trafo(), maxcalls); return MnUserParameterState(tmp, fcn.Up(), state.Trafo()); } void MnHesse::operator()(const FCNBase &fcn, FunctionMinimum &min, unsigned int maxcalls) const { // interface from FunctionMinimum to be used after minimization // use last state from the minimization without the need to re-create a new state // do not reset function calls and keep updating them MnUserFcn mfcn(fcn, min.UserState().Trafo(), min.NFcn()); MinimumState st = (*this)(mfcn, min.State(), min.UserState().Trafo(), maxcalls); min.Add(st); } MinimumState MnHesse::operator()(const MnFcn &mfcn, const MinimumState &st, const MnUserTransformation &trafo, unsigned int maxcalls) const { // internal interface from MinimumState and MnUserTransformation // Function who does the real Hessian calculations MnPrint print("MnHesse"); const MnMachinePrecision &prec = trafo.Precision(); // make sure starting at the right place double amin = mfcn(st.Vec()); double aimsag = std::sqrt(prec.Eps2()) * (std::fabs(amin) + mfcn.Up()); // diagonal Elements first unsigned int n = st.Parameters().Vec().size(); if (maxcalls == 0) maxcalls = 200 + 100 * n + 5 * n * n; MnAlgebraicSymMatrix vhmat(n); MnAlgebraicVector g2 = st.Gradient().G2(); MnAlgebraicVector gst = st.Gradient().Gstep(); MnAlgebraicVector grd = st.Gradient().Grad(); MnAlgebraicVector dirin = st.Gradient().Gstep(); MnAlgebraicVector yy(n); // case gradient is not numeric (could be analytical or from FumiliGradientCalculator) if (st.Gradient().IsAnalytical()) { Numerical2PGradientCalculator igc(mfcn, trafo, fStrategy); FunctionGradient tmp = igc(st.Parameters()); gst = tmp.Gstep(); dirin = tmp.Gstep(); g2 = tmp.G2(); } MnAlgebraicVector x = st.Parameters().Vec(); print.Debug("Gradient is", st.Gradient().IsAnalytical() ? "analytical" : "numerical", "\n point:", x, "\n fcn :", amin, "\n grad :", grd, "\n step :", gst, "\n g2 :", g2); for (unsigned int i = 0; i < n; i++) { double xtf = x(i); double dmin = 8. * prec.Eps2() * (std::fabs(xtf) + prec.Eps2()); double d = std::fabs(gst(i)); if (d < dmin) d = dmin; print.Debug("Derivative parameter", i, "d =", d, "dmin =", dmin); for (unsigned int icyc = 0; icyc < Ncycles(); icyc++) { double sag = 0.; double fs1 = 0.; double fs2 = 0.; for (unsigned int multpy = 0; multpy < 5; multpy++) { x(i) = xtf + d; fs1 = mfcn(x); x(i) = xtf - d; fs2 = mfcn(x); x(i) = xtf; sag = 0.5 * (fs1 + fs2 - 2. * amin); print.Debug("cycle", icyc, "mul", multpy, "\tsag =", sag, "d =", d); // Now as F77 Minuit - check that sag is not zero if (sag != 0) goto L30; // break if (trafo.Parameter(i).HasLimits()) { if (d > 0.5) goto L26; d *= 10.; if (d > 0.5) d = 0.51; continue; } d *= 10.; } L26: // get parameter name for i // (need separate scope for avoiding compl error when declaring name) print.Warn("2nd derivative zero for parameter", trafo.Name(trafo.ExtOfInt(i)), "; MnHesse fails and will return diagonal matrix"); for (unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j); vhmat(j, j) = tmp < prec.Eps2() ? 1. : tmp; } return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed), st.Gradient(), st.Edm(), mfcn.NumOfCalls()); L30: double g2bfor = g2(i); g2(i) = 2. * sag / (d * d); grd(i) = (fs1 - fs2) / (2. * d); gst(i) = d; dirin(i) = d; yy(i) = fs1; double dlast = d; d = std::sqrt(2. * aimsag / std::fabs(g2(i))); if (trafo.Parameter(i).HasLimits()) d = std::min(0.5, d); if (d < dmin) d = dmin; print.Debug("g1 =", grd(i), "g2 =", g2(i), "step =", gst(i), "d =", d, "diffd =", std::fabs(d - dlast) / d, "diffg2 =", std::fabs(g2(i) - g2bfor) / g2(i)); // see if converged if (std::fabs((d - dlast) / d) < Tolerstp()) break; if (std::fabs((g2(i) - g2bfor) / g2(i)) < TolerG2()) break; d = std::min(d, 10. * dlast); d = std::max(d, 0.1 * dlast); } vhmat(i, i) = g2(i); if (mfcn.NumOfCalls() > maxcalls) { // std::cout<<"maxcalls " << maxcalls << " " << mfcn.NumOfCalls() << " " << st.NFcn() << std::endl; print.Warn("Maximum number of allowed function calls exhausted; will return diagonal matrix"); for (unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j); vhmat(j, j) = tmp < prec.Eps2() ? 1. : tmp; } return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnReachedCallLimit), st.Gradient(), st.Edm(), mfcn.NumOfCalls()); } } print.Debug("Second derivatives", g2); if (fStrategy.Strategy() > 0) { // refine first derivative HessianGradientCalculator hgc(mfcn, trafo, fStrategy); FunctionGradient gr = hgc(st.Parameters(), FunctionGradient(grd, g2, gst)); // update gradient and step values grd = gr.Grad(); gst = gr.Gstep(); } // off-diagonal Elements // initial starting values if (n > 0) { MPIProcess mpiprocOffDiagonal(n * (n - 1) / 2, 0); unsigned int startParIndexOffDiagonal = mpiprocOffDiagonal.StartElementIndex(); unsigned int endParIndexOffDiagonal = mpiprocOffDiagonal.EndElementIndex(); unsigned int offsetVect = 0; for (unsigned int in = 0; in < startParIndexOffDiagonal; in++) if ((in + offsetVect) % (n - 1) == 0) offsetVect += (in + offsetVect) / (n - 1); for (unsigned int in = startParIndexOffDiagonal; in < endParIndexOffDiagonal; in++) { int i = (in + offsetVect) / (n - 1); if ((in + offsetVect) % (n - 1) == 0) offsetVect += i; int j = (in + offsetVect) % (n - 1) + 1; if ((i + 1) == j || in == startParIndexOffDiagonal) x(i) += dirin(i); x(j) += dirin(j); double fs1 = mfcn(x); double elem = (fs1 + amin - yy(i) - yy(j)) / (dirin(i) * dirin(j)); vhmat(i, j) = elem; x(j) -= dirin(j); if (j % (n - 1) == 0 || in == endParIndexOffDiagonal - 1) x(i) -= dirin(i); } mpiprocOffDiagonal.SyncSymMatrixOffDiagonal(vhmat); } // verify if matrix pos-def (still 2nd derivative) print.Debug("Original error matrix", vhmat); MinimumError tmpErr = MnPosDef()(MinimumError(vhmat, 1.), prec); vhmat = tmpErr.InvHessian(); print.Debug("PosDef error matrix", vhmat); int ifail = Invert(vhmat); if (ifail != 0) { print.Warn("Matrix inversion fails; will return diagonal matrix"); MnAlgebraicSymMatrix tmpsym(vhmat.Nrow()); for (unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j); tmpsym(j, j) = tmp < prec.Eps2() ? 1. : tmp; } return MinimumState(st.Parameters(), MinimumError(tmpsym, MinimumError::MnInvertFailed), st.Gradient(), st.Edm(), mfcn.NumOfCalls()); } FunctionGradient gr(grd, g2, gst); VariableMetricEDMEstimator estim; // if matrix is made pos def returns anyway edm if (tmpErr.IsMadePosDef()) { MinimumError err(vhmat, MinimumError::MnMadePosDef); double edm = estim.Estimate(gr, err); return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls()); } // calculate edm for good errors MinimumError err(vhmat, 0.); double edm = estim.Estimate(gr, err); print.Debug("Hessian is ACCURATE. New state:", "\n First derivative:", grd, "\n Second derivative:", g2, "\n Gradient step:", gst, "\n Covariance matrix:", vhmat, "\n Edm:", edm); return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls()); } /* MinimumError MnHesse::Hessian(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo) const { const MnMachinePrecision& prec = trafo.Precision(); // make sure starting at the right place double amin = mfcn(st.Vec()); // if(std::fabs(amin - st.Fval()) > prec.Eps2()) std::cout<<"function Value differs from amin by "< prec.Eps2()) break; if(trafo.Parameter(i).HasLimits()) { if(d > 0.5) { std::cout<<"second derivative zero for Parameter "< 0.5) d = 0.51; continue; } d *= 10.; } if(sag < prec.Eps2()) { std::cout<<"MnHesse: internal loop exhausted, return diagonal matrix."< #include #ifdef USE_OTHER_LS #include "Math/SMatrix.h" #include "Math/SVector.h" #include "Math/IFunction.h" #include "Math/Minimizer1D.h" #endif namespace ROOT { namespace Minuit2 { /** Perform a line search from position defined by the vector st along the direction step, where the length of vector step gives the expected position of Minimum. fcn is Value of function at the starting position , gdel (if non-zero) is df/dx along step at st. Return a parabola point containing Minimum x position and y (function Value) - add a falg to control the debug */ MnParabolaPoint MnLineSearch::operator()(const MnFcn &fcn, const MinimumParameters &st, const MnAlgebraicVector &step, double gdel, const MnMachinePrecision &prec) const { //*-*-*-*-*-*-*-*-*-*Perform a line search from position st along step *-*-*-*-*-*-*-* //*-* ========================================= //*-* SLAMBG and ALPHA control the maximum individual steps allowed. //*-* The first step is always =1. The max length of second step is SLAMBG. //*-* The max size of subsequent steps is the maximum previous successful //*-* step multiplied by ALPHA + the size of most recent successful step, //*-* but cannot be smaller than SLAMBG. //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- MnPrint print("MnLineSearch"); print.Debug("gdel", gdel, "step", step); double overal = 1000.; double undral = -100.; double toler = 0.05; double slamin = 0.; double slambg = 5.; double alpha = 2.; int maxiter = 12; // start as in Fortran from 1 and count all the time we evaluate the function int niter = 1; for (unsigned int i = 0; i < step.size(); i++) { if (step(i) == 0) continue; double ratio = std::fabs(st.Vec()(i) / step(i)); if (slamin == 0) slamin = ratio; if (ratio < slamin) slamin = ratio; } if (std::fabs(slamin) < prec.Eps()) slamin = prec.Eps(); slamin *= prec.Eps2(); double f0 = st.Fval(); double f1 = fcn(st.Vec() + step); niter++; double fvmin = st.Fval(); double xvmin = 0.; if (f1 < f0) { fvmin = f1; xvmin = 1.; } double toler8 = toler; double slamax = slambg; double flast = f1; double slam = 1.; bool iterate = false; MnParabolaPoint p0(0., f0); MnParabolaPoint p1(slam, flast); double f2 = 0.; // quadratic interpolation using the two points p0,p1 and the slope at p0 do { // cut toler8 as function goes up iterate = false; // MnParabola pb = MnParabolaFactory()(p0, gdel, p1); print.Debug("flast", flast, "f0", f0, "flast-f0", flast - f0, "slam", slam); // double df = flast-f0; // if(std::fabs(df) < prec.Eps2()) { // if(flast-f0 < 0.) df = -prec.Eps2(); // else df = prec.Eps2(); // } // std::cout<<"df= "<= maxiter) { // exhausted max number of iterations return MnParabolaPoint(xvmin, fvmin); } print.Debug("after initial 2-point iter:", '\n', " x0, x1, x2:", p0.X(), p1.X(), slam, '\n', " f0, f1, f2:", p0.Y(), p1.Y(), f2); MnParabolaPoint p2(slam, f2); // do now the quadratic interpolation with 3 points do { slamax = std::max(slamax, alpha * std::fabs(xvmin)); MnParabola pb = MnParabolaFactory()(p0, p1, p2); print.Debug("Iteration", niter, '\n', " x0, x1, x2:", p0.X(), p1.X(), p2.X(), '\n', " f0, f1, f2:", p0.Y(), p1.Y(), p2.Y(), '\n', " slamax :", slamax, '\n', " p2-p0,p1 :", p2.Y() - p0.Y(), p2.Y() - p1.Y(), '\n', " a, b, c :", pb.A(), pb.B(), pb.C()); if (pb.A() < prec.Eps2()) { double slopem = 2. * pb.A() * xvmin + pb.B(); if (slopem < 0.) slam = xvmin + slamax; else slam = xvmin - slamax; print.Debug("xvmin", xvmin, "slopem", slopem, "slam", slam); } else { slam = pb.Min(); // std::cout<<"pb.Min() slam= "< xvmin + slamax) slam = xvmin + slamax; if (slam < xvmin - slamax) slam = xvmin - slamax; } if (slam > 0.) { if (slam > overal) slam = overal; } else { if (slam < undral) slam = undral; } print.Debug("slam", slam, "undral", undral, "overal", overal); double f3 = 0.; do { print.Debug("iterate on f3- slam", niter, "slam", slam, "xvmin", xvmin); iterate = false; double toler9 = std::max(toler8, std::fabs(toler8 * slam)); // min. of parabola at one point if (std::fabs(p0.X() - slam) < toler9 || std::fabs(p1.X() - slam) < toler9 || std::fabs(p2.X() - slam) < toler9) { // std::cout<<"f1, f2, f3= "< p0.Y() && f3 > p1.Y() && f3 > p2.Y()) { print.Debug("f3 worse than all three previous"); if (slam > xvmin) overal = std::min(overal, slam - toler8); if (slam < xvmin) undral = std::max(undral, slam + toler8); slam = 0.5 * (slam + xvmin); print.Debug("new slam", slam); iterate = true; niter++; } } while (iterate && niter < maxiter); if (niter >= maxiter) { // exhausted max number of iterations return MnParabolaPoint(xvmin, fvmin); } // find worst previous point out of three and replace MnParabolaPoint p3(slam, f3); if (p0.Y() > p1.Y() && p0.Y() > p2.Y()) p0 = p3; else if (p1.Y() > p0.Y() && p1.Y() > p2.Y()) p1 = p3; else p2 = p3; print.Debug("f3", f3, "fvmin", fvmin, "xvmin", xvmin); if (f3 < fvmin) { fvmin = f3; xvmin = slam; } else { if (slam > xvmin) overal = std::min(overal, slam - toler8); if (slam < xvmin) undral = std::max(undral, slam + toler8); } niter++; } while (niter < maxiter); print.Debug("f1, f2 =", p0.Y(), p1.Y(), '\n', "x1, x2 =", p0.X(), p1.X(), '\n', "x, f =", xvmin, fvmin); return MnParabolaPoint(xvmin, fvmin); } #ifdef USE_OTHER_LS /** Perform a line search using a cubic interpolation using x0, x1 , df/dx(x0) and d2/dx(x0) (second derivative) This is used at the beginning when the second derivative is known to be negative */ MnParabolaPoint MnLineSearch::CubicSearch(const MnFcn &fcn, const MinimumParameters &st, const MnAlgebraicVector &step, double gdel, double g2del, const MnMachinePrecision &prec) const { MnPrint print("MnLineSearch::CubicSearch"); print.Debug("gdel", gdel, "g2del", g2del, "step", step); // change ot large values double overal = 100.; double undral = -100.; double toler = 0.05; double slamin = 0.; double slambg = 5.; double alpha = 2.; for (unsigned int i = 0; i < step.size(); i++) { if (step(i) == 0) continue; double ratio = std::fabs(st.Vec()(i) / step(i)); if (slamin == 0) slamin = ratio; if (ratio < slamin) slamin = ratio; } if (std::fabs(slamin) < prec.Eps()) slamin = prec.Eps(); slamin *= prec.Eps2(); double f0 = st.Fval(); double f1 = fcn(st.Vec() + step); double fvmin = st.Fval(); double xvmin = 0.; print.Debug("f0", f0, "f1", f1); if (f1 < f0) { fvmin = f1; xvmin = 1.; } double toler8 = toler; double slamax = slambg; double flast = f1; double slam = 1.; // MnParabolaPoint p0(0., f0); // MnParabolaPoint p1(slam, flast); ROOT::Math::SMatrix cubicMatrix; ROOT::Math::SVector cubicCoeff; // cubic coefficients to be found ROOT::Math::SVector bVec; // cubic coefficients to be found double x0 = 0; // cubic interpolation using the two points p0,p1 and the slope at p0 and the second derivative at p0 // cut toler8 as function goes up double x1 = slam; cubicMatrix(0, 0) = (x0 * x0 * x0 - x1 * x1 * x1) / 3.; cubicMatrix(0, 1) = (x0 * x0 - x1 * x1) / 2.; cubicMatrix(0, 2) = (x0 - x1); cubicMatrix(1, 0) = x0 * x0; cubicMatrix(1, 1) = x0; cubicMatrix(1, 2) = 1; cubicMatrix(2, 0) = 2. * x0; cubicMatrix(2, 1) = 1; cubicMatrix(2, 2) = 0; bVec(0) = f0 - f1; bVec(1) = gdel; bVec(2) = g2del; // if (debug) std::cout << "Matrix:\n " << cubicMatrix << std::endl; print.Debug("Vec:\n ", bVec); // find the minimum need to invert the matrix if (!cubicMatrix.Invert()) { print.Warn("Inversion failed - return"); return MnParabolaPoint(xvmin, fvmin); } cubicCoeff = cubicMatrix * bVec; print.Debug("Cubic:\n ", cubicCoeff); double ddd = cubicCoeff(1) * cubicCoeff(1) - 4 * cubicCoeff(0) * cubicCoeff(2); // b**2 - 4ac double slam1, slam2 = 0; // slam1 should be minimum and slam2 the maximum if (cubicCoeff(0) > 0) { slam1 = (-cubicCoeff(1) - std::sqrt(ddd)) / (2. * cubicCoeff(0)); slam2 = (-cubicCoeff(1) + std::sqrt(ddd)) / (2. * cubicCoeff(0)); } else if (cubicCoeff(0) < 0) { slam1 = (-cubicCoeff(1) + std::sqrt(ddd)) / (2. * cubicCoeff(0)); slam2 = (-cubicCoeff(1) - std::sqrt(ddd)) / (2. * cubicCoeff(0)); } else { // case == 0 (-b/c) slam1 = -gdel / g2del; slam2 = slam1; } print.Debug("slam1", slam1, "slam2", slam2); // this should be the minimum otherwise inversion failed and I should do something else if (slam2 < undral) slam2 = undral; if (slam2 > overal) slam2 = overal; // I am stack somewhere - take a large step if (std::fabs(slam2) < toler) slam2 = (slam2 >= 0) ? slamax : -slamax; double f2 = fcn(st.Vec() + slam2 * step); print.Debug("try with slam 2", slam2, "f2", f2); double fp; // use this as new minimum // bool noImpr = false; if (f2 < fvmin) { slam = slam2; xvmin = slam; fvmin = f2; fp = fvmin; } else { // try with slam2 if it is better if (slam1 < undral) slam1 = undral; if (slam1 > overal) slam1 = overal; if (std::fabs(slam1) < toler) slam1 = (slam1 >= 0) ? -slamax : slamax; double f3 = fcn(st.Vec() + slam1 * step); print.Debug("try with slam 1", slam1, "f3", f3); if (f3 < fvmin) { slam = slam1; fp = fvmin; xvmin = slam; fvmin = f3; } else { // case both f2 and f3 did not produce a better result if (f2 < f3) { slam = slam1; fp = f2; } else { slam = slam2; fp = f3; } } } bool iterate2 = false; int niter = 0; int maxiter = 10; do { iterate2 = false; print.Debug("iter", niter, "test approx deriv ad second deriv at", slam, "fp", fp); // estimate grad and second derivative at new point taking a step of 10-3 double h = 0.001 * slam; double fh = fcn(st.Vec() + (slam + h) * step); double fl = fcn(st.Vec() + (slam - h) * step); double df = (fh - fl) / (2. * h); double df2 = (fh + fl - 2. * fp) / (h * h); print.Debug("deriv", df, df2); // if I am in a point of still negative derivative if (std::fabs(df) < prec.Eps() && std::fabs(df2) < prec.Eps()) { // try in opposite direction with an opposite value slam = (slam >= 0) ? -slamax : slamax; slamax *= 10; fp = fcn(st.Vec() + slam * step); } else if (std::fabs(df2) <= 0) { // gradient is significative different than zero then redo a cubic interpolation // from new point return MnParabolaPoint(slam, fp); // should redo a cubic interpol. ?? // niter ++; // if (niter > maxiter) break; // MinimumParameters pa = MinimumParameters(st.Vec() + stepNew, fp); // gdel = stepNew(i) // MnParabolaPoint pp = CubicSearch(fcn, st, stepNew, df, df2 } else return MnParabolaPoint(slam, fp); niter++; } while (niter < maxiter); return MnParabolaPoint(xvmin, fvmin); } // class describing Fcn function in one dimension class ProjectedFcn : public ROOT::Math::IGenFunction { public: ProjectedFcn(const MnFcn &fcn, const MinimumParameters &pa, const MnAlgebraicVector &step) : fFcn(fcn), fPar(pa), fStep(step) { } ROOT::Math::IGenFunction *Clone() const { return new ProjectedFcn(*this); } private: double DoEval(double x) const { return fFcn(fPar.Vec() + x * fStep); } const MnFcn &fFcn; const MinimumParameters &fPar; const MnAlgebraicVector &fStep; }; MnParabolaPoint MnLineSearch::BrentSearch(const MnFcn &fcn, const MinimumParameters &st, const MnAlgebraicVector &step, double gdel, double g2del, const MnMachinePrecision &prec) const { MnPrint print("MnLineSearch::BrentSearch"); print.Debug("gdel", gdel, "g2del", g2del); print.Debug([&](std::ostream &os) { for (unsigned int i = 0; i < step.size(); ++i) { if (step(i) != 0) { os << "step(i) " << step(i) << '\n'; std::cout << "par(i) " << st.Vec()(i) << '\n'; break; } } }); ProjectedFcn func(fcn, st, step); // do first a cubic interpolation double f0 = st.Fval(); double f1 = fcn(st.Vec() + step); f0 = func(0.0); f1 = func(1.); double fvmin = st.Fval(); double xvmin = 0.; print.Debug("f0", f0, "f1", f1); if (f1 < f0) { fvmin = f1; xvmin = 1.; } // double toler8 = toler; // double slamax = slambg; // double flast = f1; double slam = 1.; double undral = -1000; double overal = 1000; double x0 = 0; // MnParabolaPoint p0(0., f0); // MnParabolaPoint p1(slam, flast); #ifdef USE_CUBIC ROOT::Math::SMatrix cubicMatrix; ROOT::Math::SVector cubicCoeff; // cubic coefficients to be found ROOT::Math::SVector bVec; // cubic coefficients to be found // cubic interpolation using the two points p0,p1 and the slope at p0 and the second derivative at p0 // cut toler8 as function goes up double x1 = slam; cubicMatrix(0, 0) = (x0 * x0 * x0 - x1 * x1 * x1) / 3.; cubicMatrix(0, 1) = (x0 * x0 - x1 * x1) / 2.; cubicMatrix(0, 2) = (x0 - x1); cubicMatrix(1, 0) = x0 * x0; cubicMatrix(1, 1) = x0; cubicMatrix(1, 2) = 1; cubicMatrix(2, 0) = 2. * x0; cubicMatrix(2, 1) = 1; cubicMatrix(2, 2) = 0; bVec(0) = f0 - f1; bVec(1) = gdel; bVec(2) = g2del; // if (debug) std::cout << "Matrix:\n " << cubicMatrix << std::endl; print.Debug("Vec:\n ", bVec); // find the minimum need to invert the matrix if (!cubicMatrix.Invert()) { print.Warn("Inversion failed - return"); return MnParabolaPoint(xvmin, fvmin); } cubicCoeff = cubicMatrix * bVec; print.Debug("Cubic:\n ", cubicCoeff); double ddd = cubicCoeff(1) * cubicCoeff(1) - 4 * cubicCoeff(0) * cubicCoeff(2); // b**2 - 4ac double slam1, slam2 = 0; // slam1 should be minimum and slam2 the maximum if (cubicCoeff(0) > 0) { slam1 = (-cubicCoeff(1) - std::sqrt(ddd)) / (2. * cubicCoeff(0)); slam2 = (-cubicCoeff(1) + std::sqrt(ddd)) / (2. * cubicCoeff(0)); } else if (cubicCoeff(0) < 0) { slam1 = (-cubicCoeff(1) + std::sqrt(ddd)) / (2. * cubicCoeff(0)); slam2 = (-cubicCoeff(1) - std::sqrt(ddd)) / (2. * cubicCoeff(0)); } else { // case == 0 (-b/c) slam1 = -gdel / g2del; slam2 = slam1; } if (slam1 < undral) slam1 = undral; if (slam1 > overal) slam1 = overal; if (slam2 < undral) slam2 = undral; if (slam2 > overal) slam2 = overal; double fs1 = func(slam1); double fs2 = func(slam2); print.Debug("slam1", slam1, "slam2", slam2, "f(slam1)", fs1, "f(slam2)", fs2); if (fs1 < fs2) { x0 = slam1; f0 = fs1; } else { x0 = slam2; f0 = fs2; } #else x0 = xvmin; f0 = fvmin; #endif double astart = 100; // do a Brent search in a large interval double a = x0 - astart; double b = x0 + astart; // double x0 = 1; int maxiter = 20; double absTol = 0.5; double relTol = 0.05; ROOT::Math::Minim1D::Type minType = ROOT::Math::Minim1D::BRENT; bool iterate = false; int iter = 0; print.Debug("f(0)", func(0.), "f1", func(1.0), "f(3)", func(3.0), "f(5)", func(5.0)); double fa = func(a); double fb = func(b); // double f0 = func(x0); double toler = 0.01; double delta0 = 5; double delta = delta0; bool enlarge = true; bool scanmin = false; double x2, f2 = 0; double dir = 1; int nreset = 0; do { print.Debug("iter", iter, "a", a, "b", b, "x0", x0, "fa", fa, "fb", fb, "f0", f0); if (fa <= f0 || fb <= f0) { scanmin = false; if (fa < fb) { if (fa < fvmin) { fvmin = fa; xvmin = a; } // double f2 = fa; // double x2 = a; if (enlarge) { x2 = a - 1000; // move lower f2 = func(x2); } if (std::fabs((fa - f2) / (a - x2)) > toler) { // significant change in f continue to enlarge interval x0 = a; f0 = fa; a = x2; fa = f2; enlarge = true; } else { // move direction of [a // values change a little, start from central point try with x0 = x0 - delta if (nreset == 0) dir = -1; enlarge = false; x0 = x0 + dir * delta; f0 = func(x0); // if reached limit try opposite direction , direction b] if (std::fabs((f0 - fa) / (x0 - a)) < toler) { delta = 10 * delta0 / (10. * (nreset + 1)); // decrease the delta if still not change observed a = x0; fa = f0; x0 = delta; f0 = func(x0); dir *= -1; nreset++; print.Info("A: Done a reset - scan in opposite direction!"); } delta *= 2; // double delta at next iteration if (x0 < b && x0 > a) scanmin = true; else { x0 = 0; f0 = st.Fval(); } } } else { // fb < fa if (fb < fvmin) { fvmin = fb; xvmin = b; } if (enlarge) { x2 = b + 1000; // move upper f2 = func(x2); } if (std::fabs((fb - f2) / (x2 - b)) > toler) { // significant change in f continue to enlarge interval f0 = fb; x0 = b; b = x2; // fb = f2; enlarge = true; } else { // here move direction b // values change a little, try with x0 = fa + delta if (nreset == 0) dir = 1; enlarge = false; x0 = x0 + dir * delta; f0 = func(x0); // if reached limit try other side if (std::fabs((f0 - fb) / (x0 - b)) < toler) { delta = 10 * delta0 / (10. * (nreset + 1)); // decrease the delta if still not change observed b = x0; fb = f0; x0 = -delta; f0 = func(x0); dir *= -1; nreset++; print.Info("B: Done a reset - scan in opposite direction!"); } delta *= 2; // double delta at next iteration if (x0 > a && x0 < b) scanmin = true; else { x0 = 0; f0 = st.Fval(); } } } if (f0 < fvmin) { x0 = xvmin; fvmin = f0; } print.Debug("new x0", x0, "f0", f0); // use golden section iterate = scanmin || enlarge; } else { // x0 is a min of [a,b] iterate = false; } iter++; } while (iterate && iter < maxiter); if (f0 > fa || f0 > fb) // skip minim 1d try Minuit LS // return (*this) (fcn, st, step, gdel, prec, debug); return MnParabolaPoint(xvmin, fvmin); print.Info("1D minimization using", minType); ROOT::Math::Minimizer1D min(minType); min.SetFunction(func, x0, a, b); int ret = min.Minimize(maxiter, absTol, relTol); MnPrint::info("result of GSL 1D minimization:", ret, "niter", min.Iterations(), "xmin", min.XMinimum(), "fmin", min.FValMinimum()); return MnParabolaPoint(min.XMinimum(), min.FValMinimum()); } #endif } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnMachinePrecision.cxx0000644000000000000000000000340314332717401022043 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnTiny.h" #include namespace ROOT { namespace Minuit2 { MnMachinePrecision::MnMachinePrecision() { // use double precision values from the numeric_limits standard // and do not determine it anymore using ComputePrecision // epsilon from stundard // note that there is a factor of 2 in the definition of // std::numeric_limitys::epsilon w.r.t DLAMCH epsilon fEpsMac = 4. * std::numeric_limits::epsilon(); fEpsMa2 = 2. * std::sqrt(fEpsMac); } void MnMachinePrecision::ComputePrecision() { fEpsMac = 4.0E-7; fEpsMa2 = 2. * std::sqrt(fEpsMac); // determine machine precision using // code similar to DLAMCH LAPACK Fortran function /* char e[] = {"e"}; fEpsMac = 8.*dlamch_(e); fEpsMa2 = 2.*std::sqrt(fEpsMac); */ MnTiny mytiny; // calculate machine precision double epstry = 0.5; double epsbak = 0.; volatile double epsp1 = 0.; // allow to run this method with fast-math double one = 1.0; for (int i = 0; i < 100; i++) { epstry *= 0.5; epsp1 = one + epstry; epsbak = mytiny(epsp1); if (epsbak < epstry) { fEpsMac = 8. * epstry; fEpsMa2 = 2. * std::sqrt(fEpsMac); break; } } } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnMinos.cxx0000644000000000000000000001604714332717401017720 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnMinos.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnFunctionCross.h" #include "Minuit2/MnCross.h" #include "Minuit2/MinosError.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { MnMinos::MnMinos(const FCNBase &fcn, const FunctionMinimum &min, unsigned int stra) : fFCN(fcn), fMinimum(min), fStrategy(MnStrategy(stra)) { MnPrint print("MnMinos"); // construct from FCN + Minimum // check if Error definition has been changed, in case re-update errors if (fcn.Up() != min.Up()) { print.Warn("MnMinos: UP value has changed, need to update FunctionMinimum class"); } } MnMinos::MnMinos(const FCNBase &fcn, const FunctionMinimum &min, const MnStrategy &stra) : fFCN(fcn), fMinimum(min), fStrategy(stra) { MnPrint print("MnMinos"); // construct from FCN + Minimum // check if Error definition has been changed, in case re-update errors if (fcn.Up() != min.Up()) { print.Warn("UP value has changed, need to update FunctionMinimum class"); } } std::pair MnMinos::operator()(unsigned int par, unsigned int maxcalls, double toler) const { // do Minos analysis given the parameter index returning a pair for (lower,upper) errors MinosError mnerr = Minos(par, maxcalls, toler); return mnerr(); } double MnMinos::Lower(unsigned int par, unsigned int maxcalls, double toler) const { // get lower error for parameter par MnCross aopt = Loval(par, maxcalls, toler); MinosError mnerr(par, fMinimum.UserState().Value(par), aopt, MnCross()); return mnerr.Lower(); } double MnMinos::Upper(unsigned int par, unsigned int maxcalls, double toler) const { // upper error for parameter par MnCross aopt = Upval(par, maxcalls, toler); MinosError mnerr(par, fMinimum.UserState().Value(par), MnCross(), aopt); return mnerr.Upper(); } MinosError MnMinos::Minos(unsigned int par, unsigned int maxcalls, double toler) const { // do full minos error anlysis (lower + upper) for parameter par MnPrint print("MnMinos"); MnCross up = Upval(par, maxcalls, toler); print.Debug("Function calls to find upper error", up.NFcn()); MnCross lo = Loval(par, maxcalls, toler); print.Debug("Function calls to find lower error", lo.NFcn()); print.Debug("return Minos error", lo.Value(), ",", up.Value()); return MinosError(par, fMinimum.UserState().Value(par), lo, up); } MnCross MnMinos::FindCrossValue(int direction, unsigned int par, unsigned int maxcalls, double toler) const { // get crossing value in the parameter direction : // direction = + 1 upper value // direction = -1 lower value // pass now tolerance used for Migrad minimizations assert(direction == 1 || direction == -1); MnPrint print("MnMinos"); print.Info("Determination of", direction == 1 ? "upper" : "lower", "Minos error for parameter", par); assert(fMinimum.IsValid()); assert(!fMinimum.UserState().Parameter(par).IsFixed()); assert(!fMinimum.UserState().Parameter(par).IsConst()); if (maxcalls == 0) { unsigned int nvar = fMinimum.UserState().VariableParameters(); maxcalls = 2 * (nvar + 1) * (200 + 100 * nvar + 5 * nvar * nvar); } std::vector para(1, par); MnUserParameterState upar = fMinimum.UserState(); double err = direction * upar.Error(par); double val = upar.Value(par) + err; // check if we do not cross limits if (direction == 1 && upar.Parameter(par).HasUpperLimit()) { val = std::min(val, upar.Parameter(par).UpperLimit()); } if (direction == -1 && upar.Parameter(par).HasLowerLimit()) { val = std::max(val, upar.Parameter(par).LowerLimit()); } // recompute err in case it was truncated for the limit err = val - upar.Value(par); std::vector xmid(1, val); std::vector xdir(1, err); double up = fFCN.Up(); unsigned int ind = upar.IntOfExt(par); // get error matrix (methods return a copy) MnAlgebraicSymMatrix m = fMinimum.Error().Matrix(); // get internal parameters const MnAlgebraicVector &xt = fMinimum.Parameters().Vec(); // LM: change to use err**2 (m(i,i) instead of err as in F77 version double xunit = std::sqrt(up / m(ind, ind)); // LM (29/04/08) bug: change should be done in internal variables // set the initial value for the other parmaeters that we are going to fit in MnCross for (unsigned int i = 0; i < m.Nrow(); i++) { if (i == ind) continue; double xdev = xunit * m(ind, i); double xnew = xt(i) + direction * xdev; // transform to external values unsigned int ext = upar.ExtOfInt(i); double unew = upar.Int2ext(i, xnew); // take into account limits if (upar.Parameter(ext).HasUpperLimit()) { unew = std::min(unew, upar.Parameter(ext).UpperLimit()); } if (upar.Parameter(ext).HasLowerLimit()) { unew = std::max(unew, upar.Parameter(ext).LowerLimit()); } print.Debug("Parameter", ext, "is set from", upar.Value(ext), "to", unew); upar.SetValue(ext, unew); } upar.Fix(par); upar.SetValue(par, val); print.Debug("Parameter", par, "is fixed and set from", fMinimum.UserState().Value(par), "to", val, "delta =", err); MnFunctionCross cross(fFCN, upar, fMinimum.Fval(), fStrategy); MnCross aopt = cross(para, xmid, xdir, toler, maxcalls); print.Debug("aopt value found from MnFunctionCross =", aopt.Value()); const char *par_name = upar.Name(par); if (aopt.AtMaxFcn()) print.Warn("maximum number of function calls exceeded for Parameter", par_name); if (aopt.NewMinimum()) print.Warn("new Minimum found while looking for Parameter", par_name); if (direction == 1) { if (aopt.AtLimit()) print.Warn("parameter", par_name, "is at Upper limit"); if (!aopt.IsValid()) print.Warn("could not find Upper Value for Parameter", par_name); } else { if (aopt.AtLimit()) print.Warn("parameter", par_name, "is at Lower limit"); if (!aopt.IsValid()) print.Warn("could not find Lower Value for Parameter", par_name); } print.Info("end of Minos scan for", direction == 1 ? "up" : "low", "interval for parameter", upar.Name(par)); return aopt; } MnCross MnMinos::Upval(unsigned int par, unsigned int maxcalls, double toler) const { // return crossing in the lower parameter direction return FindCrossValue(1, par, maxcalls, toler); } MnCross MnMinos::Loval(unsigned int par, unsigned int maxcalls, double toler) const { // return crossing in the lower parameter direction return FindCrossValue(-1, par, maxcalls, toler); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnParabolaFactory.cxx0000644000000000000000000000431014332717401021672 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnParabolaFactory.h" #include "Minuit2/MnParabola.h" #include "Minuit2/MnParabolaPoint.h" namespace ROOT { namespace Minuit2 { MnParabola MnParabolaFactory:: operator()(const MnParabolaPoint &p1, const MnParabolaPoint &p2, const MnParabolaPoint &p3) const { // construct the parabola from 3 points p1,p2,p3 double x1 = p1.X(); double x2 = p2.X(); double x3 = p3.X(); double dx12 = x1 - x2; double dx13 = x1 - x3; double dx23 = x2 - x3; // std::cout<<"MnParabolaFactory x1, x2, x3: "<> MnParameterScan:: operator()(unsigned int par, unsigned int maxsteps, double low, double high) { // do the scan for parameter par between low and high values // if(maxsteps > 101) maxsteps = 101; std::vector> result; result.reserve(maxsteps + 1); std::vector params = fParameters.Params(); result.push_back(std::pair(params[par], fAmin)); if (low > high) return result; if (maxsteps < 2) return result; if (low == 0. && high == 0.) { low = params[par] - 2. * fParameters.Error(par); high = params[par] + 2. * fParameters.Error(par); } if (low == 0. && high == 0. && fParameters.Parameter(par).HasLimits()) { if (fParameters.Parameter(par).HasLowerLimit()) low = fParameters.Parameter(par).LowerLimit(); if (fParameters.Parameter(par).HasUpperLimit()) high = fParameters.Parameter(par).UpperLimit(); } if (fParameters.Parameter(par).HasLimits()) { if (fParameters.Parameter(par).HasLowerLimit()) low = std::max(low, fParameters.Parameter(par).LowerLimit()); if (fParameters.Parameter(par).HasUpperLimit()) high = std::min(high, fParameters.Parameter(par).UpperLimit()); } double x0 = low; double stp = (high - low) / double(maxsteps - 1); for (unsigned int i = 0; i < maxsteps; i++) { params[par] = x0 + double(i) * stp; double fval = fFCN(params); if (fval < fAmin) { fParameters.SetValue(par, params[par]); fAmin = fval; } result.push_back(std::pair(params[par], fval)); } return result; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnPlot.cxx0000644000000000000000000000421514332717401017543 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnPlot.h" namespace ROOT { namespace Minuit2 { void mnplot(double *xpt, double *ypt, char *chpt, int nxypt, int npagwd, int npagln); void MnPlot::operator()(const std::vector> &points) const { // call routine from Fortran minuit (mnplot) to plot the vector of (x,y) points std::vector x; x.reserve(points.size()); std::vector y; y.reserve(points.size()); std::vector chpt; chpt.reserve(points.size()); for (std::vector>::const_iterator ipoint = points.begin(); ipoint != points.end(); ++ipoint) { x.push_back((*ipoint).first); y.push_back((*ipoint).second); chpt.push_back('*'); } mnplot(&(x.front()), &(y.front()), &(chpt.front()), points.size(), Width(), Length()); } void MnPlot::operator()(double xmin, double ymin, const std::vector> &points) const { // call routine from Fortran minuit (mnplot) to plot the vector of (x,y) points + minimum values std::vector x; x.reserve(points.size() + 2); x.push_back(xmin); x.push_back(xmin); std::vector y; y.reserve(points.size() + 2); y.push_back(ymin); y.push_back(ymin); std::vector chpt; chpt.reserve(points.size() + 2); chpt.push_back(' '); chpt.push_back('X'); for (std::vector>::const_iterator ipoint = points.begin(); ipoint != points.end(); ++ipoint) { x.push_back((*ipoint).first); y.push_back((*ipoint).second); chpt.push_back('*'); } mnplot(&(x.front()), &(y.front()), &(chpt.front()), points.size() + 2, Width(), Length()); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnPosDef.cxx0000644000000000000000000000635414332717401020013 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnPosDef.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { LAVector eigenvalues(const LASymMatrix &); MinimumState MnPosDef::operator()(const MinimumState &st, const MnMachinePrecision &prec) const { // interface from minimum state MinimumError err = (*this)(st.Error(), prec); return MinimumState(st.Parameters(), err, st.Gradient(), st.Edm(), st.NFcn()); } MinimumError MnPosDef::operator()(const MinimumError &e, const MnMachinePrecision &prec) const { MnPrint print("MnPosDef"); // make error matrix positive defined returning a new corrected minimum error state MnAlgebraicSymMatrix err(e.InvHessian()); if (err.size() == 1 && err(0, 0) < prec.Eps()) { err(0, 0) = 1.; return MinimumError(err, MinimumError::MnMadePosDef); } if (err.size() == 1 && err(0, 0) > prec.Eps()) { return e; } // std::cout<<"MnPosDef init matrix= "< #include #include #include #include constexpr int PRECISION = 10; constexpr int WIDTH = PRECISION + 7; namespace ROOT { namespace Minuit2 { // we don't use a std::vector or std::array here, because we want a mix of the two; // a stack-allocated container with fixed capacity but dynamic size, i.e. the equivalent // of static_vector from the Boost Container library template class PrefixStack { public: using const_pointer = const T *; using const_reference = const T &; void Push(T prefix) { if (fSize < fMaxSize) fData[fSize] = prefix; else { // crop the stack when it becomes too deep as a last resort, but this should not // happen, fMaxSize should be increased instead if this occurs fData[fMaxSize - 1] = prefix; fData[fMaxSize - 2] = "..."; } ++fSize; } void Pop() { assert(fSize > 0); --fSize; } const_pointer begin() const { return fData; } const_pointer end() const { return fData + (fSize < fMaxSize ? fSize : fMaxSize); } const_reference back() const { return *(end() - 1); } private: static constexpr unsigned fMaxSize = 10; // increase as needed T fData[fMaxSize]; unsigned fSize = 0; }; // gShowPrefixStack determines how messages are printed, it acts on all threads; // race conditions when writing to this do not cause failures bool gShowPrefixStack = false; // writing to gPrefixFilter is not thread-safe, should be done only from main thread std::vector gPrefixFilter; // gPrintLevel must be thread-local, because it may be manipulated by a thread to // temporarily turn logging on or off; Minuit2Minimizer does this, for example thread_local int gPrintLevel = 0; // gPrefixStack must be thread-local thread_local PrefixStack gPrefixStack; MnPrint::MnPrint(const char *prefix, int level) : fLevel{level} { gPrefixStack.Push(prefix); } MnPrint::~MnPrint() { gPrefixStack.Pop(); } void MnPrint::ShowPrefixStack(bool yes) { gShowPrefixStack = yes; } void MnPrint::AddFilter(const char *filter) { gPrefixFilter.emplace_back(filter); } void MnPrint::ClearFilter() { gPrefixFilter.clear(); } int MnPrint::SetGlobalLevel(int level) { // should use std::exchange or boost::exchange std::swap(gPrintLevel, level); return level; } int MnPrint::GlobalLevel() { return gPrintLevel; } int MnPrint::SetLevel(int level) { // should use std::exchange or boost::exchange std::swap(fLevel, level); return level; } int MnPrint::Level() const { return fLevel; } void StreamFullPrefix(std::ostringstream &os) { const char *prev = ""; for (const auto cs : gPrefixStack) { // skip repeated prefixes; repetition happens when class method calls another // method of the same class and both set up a MnPrint instance if (std::strcmp(cs, prev) != 0) os << cs << ":"; prev = cs; } } void MnPrint::StreamPrefix(std::ostringstream &os) { if (gShowPrefixStack) { // show full prefix stack, useful to set sharp filters and to see what calls what StreamFullPrefix(os); } else { // show only the top of the prefix stack (the prefix of the innermost scope) os << gPrefixStack.back(); } } bool MnPrint::Hidden() { // Filtering is not implemented a very efficient way to keep it simple, but the // implementation ensures that the performance drop is opt-in. Only when filters are // used there is a performance loss. // The intended use case of filtering is for debugging, when highest performance // does not matter. Filtering is only every attempted if the message passes the // threshold level. // Filtering is very fast when the filter is empty. if (gPrefixFilter.empty()) return false; std::ostringstream os; os << "^"; StreamFullPrefix(os); std::string prefix = os.str(); // Filtering works like grep, the message is shown if any of the filter strings match. // To only match the beginning of the prefix, use "^". For example "^MnHesse" only // matches direct execution of MnHesse, but not MnHesse called by MnMigrad. for (const auto &s : gPrefixFilter) { if (prefix.find(s) != std::string::npos) return false; } return true; } MnPrint::Oneline::Oneline(double fcn, double edm, int ncalls, int iter) : fFcn(fcn), fEdm(edm), fNcalls(ncalls), fIter(iter) { } MnPrint::Oneline::Oneline(const MinimumState &state, int iter) : MnPrint::Oneline(state.Fval(), state.Edm(), state.NFcn(), iter) { } MnPrint::Oneline::Oneline(const FunctionMinimum &fmin, int iter) : MnPrint::Oneline(fmin.State(), iter) {} std::ostream &operator<<(std::ostream &os, const MnPrint::Oneline &x) { // print iteration, function value, edm and ncalls in one single line if (x.fIter >= 0) os << std::setw(4) << x.fIter << " - "; const int pr = os.precision(PRECISION); os << "FCN = " << std::setw(WIDTH) << x.fFcn << " Edm = " << std::setw(WIDTH) << x.fEdm << " NCalls = " << std::setw(6) << x.fNcalls; os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const LAVector &vec) { // print a vector const int pr = os.precision(PRECISION); const int nrow = vec.size(); for (int i = 0; i < nrow; i++) { os << '\n'; os.width(WIDTH); os << vec(i); } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const LASymMatrix &matrix) { // print a matrix const int pr = os.precision(8); const int n = matrix.Nrow(); for (int i = 0; i < n; i++) { os << '\n'; for (int j = 0; j < n; j++) { os.width(15); os << matrix(i, j); } } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MnUserParameters &par) { // print the MnUserParameter object os << "\n Pos | Name | type | Value | Error +/-"; int pr = os.precision(); const double eps2 = par.Precision().Eps2(); for (auto &&p : par.Parameters()) { os << "\n" << std::setw(5) << p.Number() << " | " << std::setw(10) << p.Name() << " |"; if (p.IsConst()) os << " const |"; else if (p.IsFixed()) os << " fixed |"; else if (p.HasLimits()) os << " limited |"; else os << " free |"; os.precision(PRECISION); os.width(WIDTH); os << p.Value() << " | " << std::setw(12); if (p.Error() > 0) { os << p.Error(); if (p.HasLimits()) { if (std::fabs(p.Value() - p.LowerLimit()) < eps2) { os << " (at lower limit)"; } else if (std::fabs(p.Value() - p.UpperLimit()) < eps2) { os << " (at upper limit)"; } } } } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MnUserCovariance &matrix) { // print the MnUserCovariance const int pr = os.precision(6); unsigned int n = matrix.Nrow(); for (unsigned int i = 0; i < n; i++) { os << '\n'; for (unsigned int j = 0; j < n; j++) { os.width(13); os << matrix(i, j); } os << " | "; double di = matrix(i, i); for (unsigned int j = 0; j < n; j++) { double dj = matrix(j, j); os.width(13); os << matrix(i, j) / std::sqrt(std::fabs(di * dj)); } } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MnGlobalCorrelationCoeff &coeff) { // print the global correlation coefficient const int pr = os.precision(6); for (auto &&x : coeff.GlobalCC()) { os << '\n'; os.width(6 + 7); os << x; } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MnUserParameterState &state) { // print the MnUserParameterState const int pr = os.precision(PRECISION); os << "\n Valid : " << (state.IsValid() ? "yes" : "NO") << "\n Function calls: " << state.NFcn() << "\n Minimum value : " << state.Fval() << "\n Edm : " << state.Edm() << "\n Parameters : " << state.Parameters() << "\n CovarianceStatus: " << state.CovarianceStatus() << "\n Covariance and correlation matrix: "; if (state.HasCovariance()) os << state.Covariance(); else os << "matrix is not present or not valid"; if (state.HasGlobalCC()) os << "\n Global correlation coefficients: " << state.GlobalCC(); os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const FunctionMinimum &min) { // print the FunctionMinimum const int pr = os.precision(PRECISION); os << "\n Valid : " << (min.IsValid() ? "yes" : "NO") << "\n Function calls: " << min.NFcn() << "\n Minimum value : " << min.Fval() << "\n Edm : " << min.Edm() << "\n Internal parameters: " << min.Parameters().Vec(); if (min.HasValidCovariance()) os << "\n Internal covariance matrix: " << min.Error().Matrix(); os << "\n External parameters: " << min.UserParameters(); // os << min.UserCovariance() << '\n'; // os << min.UserState().GlobalCC() << '\n'; if (!min.IsValid()) { os << "\n FunctionMinimum is invalid:"; if (!min.State().IsValid()) os << "\n State is invalid"; if (min.IsAboveMaxEdm()) os << "\n Edm is above max"; if (min.HasReachedCallLimit()) os << "\n Reached call limit"; } os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MinimumState &min) { const int pr = os.precision(PRECISION); os << "\n Minimum value : " << min.Fval() << "\n Edm : " << min.Edm() << "\n Internal parameters:" << min.Vec() << "\n Internal gradient :" << min.Gradient().Vec(); if (min.HasCovariance()) os << "\n Internal covariance matrix:" << min.Error().Matrix(); os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MnMachinePrecision &prec) { // print the Precision int pr = os.precision(PRECISION); os << "MnMachinePrecision " << prec.Eps() << '\n'; os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const MinosError &me) { // print the Minos Error os << "Minos # of function calls: " << me.NFcn() << '\n'; if (!me.IsValid()) os << "Minos Error is not valid." << '\n'; if (!me.LowerValid()) os << "lower Minos Error is not valid." << '\n'; if (!me.UpperValid()) os << "upper Minos Error is not valid." << '\n'; if (me.AtLowerLimit()) os << "Minos Error is Lower limit of Parameter " << me.Parameter() << "." << '\n'; if (me.AtUpperLimit()) os << "Minos Error is Upper limit of Parameter " << me.Parameter() << "." << '\n'; if (me.AtLowerMaxFcn()) os << "Minos number of function calls for Lower Error exhausted." << '\n'; if (me.AtUpperMaxFcn()) os << "Minos number of function calls for Upper Error exhausted." << '\n'; if (me.LowerNewMin()) { os << "Minos found a new Minimum in negative direction." << '\n'; os << me.LowerState() << '\n'; } if (me.UpperNewMin()) { os << "Minos found a new Minimum in positive direction." << '\n'; os << me.UpperState() << '\n'; } int pr = os.precision(); os << "No |" << "| Name |" << "| Value@min |" << "| negative |" << "| positive " << '\n'; os << std::setw(4) << me.Parameter() << std::setw(5) << "||"; os << std::setw(10) << me.LowerState().Name(me.Parameter()) << std::setw(3) << "||"; os << std::setprecision(PRECISION) << std::setw(WIDTH) << me.Min() << " ||" << std::setprecision(PRECISION) << std::setw(WIDTH) << me.Lower() << " ||" << std::setw(WIDTH) << me.Upper() << '\n'; os << '\n'; os.precision(pr); return os; } std::ostream &operator<<(std::ostream &os, const ContoursError &ce) { // print the ContoursError os << "Contours # of function calls: " << ce.NFcn() << '\n'; os << "MinosError in x: " << '\n'; os << ce.XMinosError() << '\n'; os << "MinosError in y: " << '\n'; os << ce.YMinosError() << '\n'; MnPlot plot; plot(ce.XMin(), ce.YMin(), ce()); for (auto ipar = ce().begin(); ipar != ce().end(); ++ipar) { os << ipar - ce().begin() << " " << (*ipar).first << " " << (*ipar).second << '\n'; } os << '\n'; return os; } std::ostream &operator<<(std::ostream &os, const std::pair &point) { os << "\t x = " << point.first << " y = " << point.second << std::endl; return os; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnPrintImpl.cxx0000644000000000000000000000147414332717401020547 0ustar00#include "Minuit2/MnPrint.h" using ROOT::Minuit2::MnPrint; #ifndef USE_ROOT_ERROR #include #ifndef MN_OS #define MN_OS std::cerr #endif void MnPrint::Impl(MnPrint::Verbosity level, const std::string &s) { const char *label[4] = {"[Error]", "[Warn]", "[Info]", "[Debug]"}; const int ilevel = static_cast(level); MN_OS << label[ilevel] << " " << s << std::endl; } #else // use ROOT error reporting system #include "TError.h" #include void MnPrint::Impl(MnPrint::Verbosity level, const std::string &s) { switch (level) { case MnPrint::eError: ::Error("Minuit2", "%s", s.c_str()); break; case MnPrint::eWarn: ::Warning("Minuit2", "%s", s.c_str()); break; case MnPrint::eInfo: case MnPrint::eDebug: ::Info("Minuit2", "%s", s.c_str()); break; } } #endif // USE_ROOT_ERROR iminuit-2.24.0/extern/root/math/minuit2/src/MnScan.cxx0000644000000000000000000000210014332717401017500 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnScan.h" #include "Minuit2/MnParameterScan.h" namespace ROOT { namespace Minuit2 { std::vector> MnScan::Scan(unsigned int par, unsigned int maxsteps, double low, double high) { // perform a scan of the function in the parameter par MnParameterScan scan(fFCN, fState.Parameters()); double amin = scan.Fval(); std::vector> result = scan(par, maxsteps, low, high); if (scan.Fval() < amin) { fState.SetValue(par, scan.Parameters().Value(par)); amin = scan.Fval(); } return result; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnSeedGenerator.cxx0000644000000000000000000001411214332717401021351 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnSeedGenerator.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/MnFcn.h" #include "Minuit2/GradientCalculator.h" #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MinimumError.h" #include "Minuit2/MnMatrix.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MinuitParameter.h" #include "Minuit2/MnLineSearch.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnHesse.h" #include "Minuit2/VariableMetricEDMEstimator.h" #include "Minuit2/NegativeG2LineSearch.h" #include "Minuit2/AnalyticalGradientCalculator.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/HessianGradientCalculator.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { MinimumSeed MnSeedGenerator:: operator()(const MnFcn &fcn, const GradientCalculator &gc, const MnUserParameterState &st, const MnStrategy &stra) const { MnPrint print("MnSeedGenerator"); // find seed (initial minimization point) using the calculated gradient const unsigned int n = st.VariableParameters(); const MnMachinePrecision &prec = st.Precision(); print.Debug(n, "free parameters, FCN pointer", &fcn); // initial starting values MnAlgebraicVector x(n); for (unsigned int i = 0; i < n; i++) x(i) = st.IntParameters()[i]; double fcnmin = fcn(x); MinimumParameters pa(x, fcnmin); FunctionGradient dgrad = gc(pa); MnAlgebraicSymMatrix mat(n); double dcovar = 1.; if (st.HasCovariance()) { for (unsigned int i = 0; i < n; i++) for (unsigned int j = i; j < n; j++) mat(i, j) = st.IntCovariance()(i, j); dcovar = 0.; } else { for (unsigned int i = 0; i < n; i++) mat(i, i) = (std::fabs(dgrad.G2()(i)) > prec.Eps2() ? 1. / dgrad.G2()(i) : 1.); } MinimumError err(mat, dcovar); double edm = VariableMetricEDMEstimator().Estimate(dgrad, err); MinimumState state(pa, err, dgrad, edm, fcn.NumOfCalls()); print.Info("Initial state:", MnPrint::Oneline(state)); NegativeG2LineSearch ng2ls; if (ng2ls.HasNegativeG2(dgrad, prec)) { print.Debug("Negative G2 Found", "\n point:", x, "\n grad :", dgrad.Grad(), "\n g2 :", dgrad.G2()); state = ng2ls(fcn, state, gc, prec); print.Info("Negative G2 found - new state:", state); } if (stra.Strategy() == 2 && !st.HasCovariance()) { // calculate full 2nd derivative print.Debug("calling MnHesse"); MinimumState tmp = MnHesse(stra)(fcn, state, st.Trafo()); print.Info("run Hesse - new state:", tmp); return MinimumSeed(tmp, st.Trafo()); } return MinimumSeed(state, st.Trafo()); } MinimumSeed MnSeedGenerator::operator()(const MnFcn &fcn, const AnalyticalGradientCalculator &gc, const MnUserParameterState &st, const MnStrategy &stra) const { MnPrint print("MnSeedGenerator"); // find seed (initial point for minimization) using analytical gradient unsigned int n = st.VariableParameters(); const MnMachinePrecision &prec = st.Precision(); // initial starting values MnAlgebraicVector x(n); for (unsigned int i = 0; i < n; i++) x(i) = st.IntParameters()[i]; double fcnmin = fcn(x); MinimumParameters pa(x, fcnmin); InitialGradientCalculator igc(fcn, st.Trafo(), stra); FunctionGradient tmp = igc(pa); FunctionGradient grd = gc(pa); FunctionGradient dgrad(grd.Grad(), tmp.G2(), tmp.Gstep()); if (gc.CheckGradient()) { bool good = true; HessianGradientCalculator hgc(fcn, st.Trafo(), MnStrategy(2)); std::pair hgrd = hgc.DeltaGradient(pa, dgrad); for (unsigned int i = 0; i < n; i++) { if (std::fabs(hgrd.first.Grad()(i) - grd.Grad()(i)) > hgrd.second(i)) { int externalParameterIndex = st.Trafo().ExtOfInt(i); const char *parameter_name = st.Trafo().Name(externalParameterIndex); print.Warn("Gradient discrepancy of external Parameter too large:" "parameter_name =", parameter_name, "externalParameterIndex =", externalParameterIndex, "internal =", i); good = false; } } if (!good) { print.Error("Minuit does not accept user specified Gradient. To force acceptance, override 'virtual bool " "CheckGradient() const' of FCNGradientBase.h in the derived class."); assert(good); } } MnAlgebraicSymMatrix mat(n); double dcovar = 1.; if (st.HasCovariance()) { for (unsigned int i = 0; i < n; i++) for (unsigned int j = i; j < n; j++) mat(i, j) = st.IntCovariance()(i, j); dcovar = 0.; } else { for (unsigned int i = 0; i < n; i++) mat(i, i) = (std::fabs(dgrad.G2()(i)) > prec.Eps2() ? 1. / dgrad.G2()(i) : 1.); } MinimumError err(mat, dcovar); double edm = VariableMetricEDMEstimator().Estimate(dgrad, err); MinimumState state(pa, err, dgrad, edm, fcn.NumOfCalls()); NegativeG2LineSearch ng2ls; if (ng2ls.HasNegativeG2(dgrad, prec)) { Numerical2PGradientCalculator ngc(fcn, st.Trafo(), stra); state = ng2ls(fcn, state, ngc, prec); } if (stra.Strategy() == 2 && !st.HasCovariance()) { // calculate full 2nd derivative MinimumState tmpState = MnHesse(stra)(fcn, state, st.Trafo()); return MinimumSeed(tmpState, st.Trafo()); } return MinimumSeed(state, st.Trafo()); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnStrategy.cxx0000644000000000000000000000342614332717401020432 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnStrategy.h" namespace ROOT { namespace Minuit2 { MnStrategy::MnStrategy() : fStoreLevel(1) { // default strategy SetMediumStrategy(); } MnStrategy::MnStrategy(unsigned int stra) : fStoreLevel(1) { // user defined strategy (0, 1, >=2) if (stra == 0) SetLowStrategy(); else if (stra == 1) SetMediumStrategy(); else SetHighStrategy(); } void MnStrategy::SetLowStrategy() { // set low strategy (0) values fStrategy = 0; SetGradientNCycles(2); SetGradientStepTolerance(0.5); SetGradientTolerance(0.1); SetHessianNCycles(3); SetHessianStepTolerance(0.5); SetHessianG2Tolerance(0.1); SetHessianGradientNCycles(1); } void MnStrategy::SetMediumStrategy() { // set minimum strategy (1) the default fStrategy = 1; SetGradientNCycles(3); SetGradientStepTolerance(0.3); SetGradientTolerance(0.05); SetHessianNCycles(5); SetHessianStepTolerance(0.3); SetHessianG2Tolerance(0.05); SetHessianGradientNCycles(2); } void MnStrategy::SetHighStrategy() { // set high strategy (2) fStrategy = 2; SetGradientNCycles(5); SetGradientStepTolerance(0.1); SetGradientTolerance(0.02); SetHessianNCycles(7); SetHessianStepTolerance(0.1); SetHessianG2Tolerance(0.02); SetHessianGradientNCycles(6); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnTiny.cxx0000644000000000000000000000142414332717401017547 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnTiny.h" namespace ROOT { namespace Minuit2 { double MnTiny::One() const { return fOne; } double MnTiny::operator()(volatile double epsp1) const { // evaluate minimal diference between two floating points double result = epsp1 - One(); return result; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnTraceObject.cxx0000644000000000000000000000332114332717401021007 0ustar00// @(#)root/minuit2:$Id$ // Author: L. Moneta 2012 /********************************************************************** * * * Copyright (c) 2012 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnTraceObject.h" #include "Minuit2/MnPrint.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MinimumState.h" #include namespace ROOT { namespace Minuit2 { void MnTraceObject::operator()(int iter, const MinimumState &state) { MnPrint print("MnTraceObject"); print.Debug(MnPrint::Oneline(state, iter)); if (!fUserState) return; print.Debug([&](std::ostream &os) { // print also parameters and derivatives os << "\n\t" << std::setw(12) << " " << " " << std::setw(12) << " ext value " << " " << std::setw(12) << " int value " << " " << std::setw(12) << " gradient "; int firstPar = 0; int lastPar = state.Vec().size(); if (fParNumber >= 0 && fParNumber < lastPar) { firstPar = fParNumber; lastPar = fParNumber + 1; } for (int ipar = firstPar; ipar < lastPar; ++ipar) { int epar = fUserState->Trafo().ExtOfInt(ipar); double eval = fUserState->Trafo().Int2ext(ipar, state.Vec()(ipar)); os << "\n\t" << std::setw(12) << fUserState->Name(epar) << " " << std::setw(12) << eval << " " << std::setw(12) << state.Vec()(ipar) << " " << std::setw(12) << state.Gradient().Vec()(ipar); } }); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnUserFcn.cxx0000644000000000000000000000322114332717401020166 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnUserFcn.h" #include "Minuit2/FCNBase.h" #include "Minuit2/MnUserTransformation.h" namespace ROOT { namespace Minuit2 { double MnUserFcn::operator()(const MnAlgebraicVector &v) const { // call Fcn function transforming from a MnAlgebraicVector of internal values to a std::vector of external ones fNumCall++; // calling fTransform() like here was not thread safe because it was using a cached vector // return Fcn()( fTransform(v) ); // make a new thread-safe implementation creating a vector each time // a bit slower few% in stressFit and 10% in Rosenbrock function but it is negligible in big fits // get first initial values of parameter (in case some one is fixed) std::vector vpar(fTransform.InitialParValues().begin(), fTransform.InitialParValues().end()); const std::vector ¶meters = fTransform.Parameters(); unsigned int n = v.size(); for (unsigned int i = 0; i < n; i++) { int ext = fTransform.ExtOfInt(i); if (parameters[ext].HasLimits()) { vpar[ext] = fTransform.Int2ext(i, v(i)); } else { vpar[ext] = v(i); } } return Fcn()(vpar); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnUserParameterState.cxx0000644000000000000000000004316214332717401022411 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnCovarianceSqueeze.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { // // construct from user parameters (befor minimization) // MnUserParameterState::MnUserParameterState(const std::vector &par, const std::vector &err) : fValid(true), fCovarianceValid(false), fGCCValid(false), fCovStatus(-1), fFVal(0.), fEDM(0.), fNFcn(0), fParameters(MnUserParameters(par, err)), fCovariance(MnUserCovariance()), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(par), fIntCovariance(MnUserCovariance()) { } MnUserParameterState::MnUserParameterState(const MnUserParameters &par) : fValid(true), fCovarianceValid(false), fGCCValid(false), fCovStatus(-1), fFVal(0.), fEDM(0.), fNFcn(0), fParameters(par), fCovariance(MnUserCovariance()), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(std::vector()), fIntCovariance(MnUserCovariance()) { // construct from user parameters (befor minimization) for (std::vector::const_iterator ipar = MinuitParameters().begin(); ipar != MinuitParameters().end(); ++ipar) { if ((*ipar).IsConst() || (*ipar).IsFixed()) continue; if ((*ipar).HasLimits()) fIntParameters.push_back(Ext2int((*ipar).Number(), (*ipar).Value())); else fIntParameters.push_back((*ipar).Value()); } } // // construct from user parameters + errors (befor minimization) // MnUserParameterState::MnUserParameterState(const std::vector &par, const std::vector &cov, unsigned int nrow) : fValid(true), fCovarianceValid(true), fGCCValid(false), fCovStatus(-1), fFVal(0.), fEDM(0.), fNFcn(0), fParameters(MnUserParameters()), fCovariance(MnUserCovariance(cov, nrow)), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(par), fIntCovariance(MnUserCovariance(cov, nrow)) { // construct from user parameters + errors (before minimization) using std::vector for parameter error and // an // std::vector of size n*(n+1)/2 for the covariance matrix and n (rank of cov matrix) std::vector err; err.reserve(par.size()); for (unsigned int i = 0; i < par.size(); i++) { assert(fCovariance(i, i) > 0.); err.push_back(std::sqrt(fCovariance(i, i))); } fParameters = MnUserParameters(par, err); assert(fCovariance.Nrow() == VariableParameters()); } MnUserParameterState::MnUserParameterState(const std::vector &par, const MnUserCovariance &cov) : fValid(true), fCovarianceValid(true), fGCCValid(false), fCovStatus(-1), fFVal(0.), fEDM(0.), fNFcn(0), fParameters(MnUserParameters()), fCovariance(cov), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(par), fIntCovariance(cov) { // construct from user parameters + errors (before minimization) using std::vector (params) and MnUserCovariance // class std::vector err; err.reserve(par.size()); for (unsigned int i = 0; i < par.size(); i++) { assert(fCovariance(i, i) > 0.); err.push_back(std::sqrt(fCovariance(i, i))); } fParameters = MnUserParameters(par, err); assert(fCovariance.Nrow() == VariableParameters()); } MnUserParameterState::MnUserParameterState(const MnUserParameters &par, const MnUserCovariance &cov) : fValid(true), fCovarianceValid(true), fGCCValid(false), fCovStatus(-1), fFVal(0.), fEDM(0.), fNFcn(0), fParameters(par), fCovariance(cov), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(std::vector()), fIntCovariance(cov) { // construct from user parameters + errors (befor minimization) using // MnUserParameters and MnUserCovariance objects fIntCovariance.Scale(0.5); for (std::vector::const_iterator ipar = MinuitParameters().begin(); ipar != MinuitParameters().end(); ++ipar) { if ((*ipar).IsConst() || (*ipar).IsFixed()) continue; if ((*ipar).HasLimits()) fIntParameters.push_back(Ext2int((*ipar).Number(), (*ipar).Value())); else fIntParameters.push_back((*ipar).Value()); } assert(fCovariance.Nrow() == VariableParameters()); // // need to Fix that in case of limited parameters // fIntCovariance = MnUserCovariance(); // } // // MnUserParameterState::MnUserParameterState(const MinimumState &st, double up, const MnUserTransformation &trafo) : fValid(st.IsValid()), fCovarianceValid(false), fGCCValid(false), fCovStatus(-1), fFVal(st.Fval()), fEDM(st.Edm()), fNFcn(st.NFcn()), fParameters(MnUserParameters()), fCovariance(MnUserCovariance()), fGlobalCC(MnGlobalCorrelationCoeff()), fIntParameters(std::vector()), fIntCovariance(MnUserCovariance()) { // // construct from internal parameters (after minimization) // // std::cout << "build a MnUSerParameterState after minimization.." << std::endl; for (std::vector::const_iterator ipar = trafo.Parameters().begin(); ipar != trafo.Parameters().end(); ++ipar) { if ((*ipar).IsConst()) { Add((*ipar).GetName(), (*ipar).Value()); } else if ((*ipar).IsFixed()) { Add((*ipar).GetName(), (*ipar).Value(), (*ipar).Error()); if ((*ipar).HasLimits()) { if ((*ipar).HasLowerLimit() && (*ipar).HasUpperLimit()) SetLimits((*ipar).GetName(), (*ipar).LowerLimit(), (*ipar).UpperLimit()); else if ((*ipar).HasLowerLimit() && !(*ipar).HasUpperLimit()) SetLowerLimit((*ipar).GetName(), (*ipar).LowerLimit()); else SetUpperLimit((*ipar).GetName(), (*ipar).UpperLimit()); } Fix((*ipar).GetName()); } else if ((*ipar).HasLimits()) { unsigned int i = trafo.IntOfExt((*ipar).Number()); double err = st.Error().IsValid() ? std::sqrt(2. * up * st.Error().InvHessian()(i, i)) : st.Parameters().Dirin()(i); Add((*ipar).GetName(), trafo.Int2ext(i, st.Vec()(i)), trafo.Int2extError(i, st.Vec()(i), err)); if ((*ipar).HasLowerLimit() && (*ipar).HasUpperLimit()) SetLimits((*ipar).GetName(), (*ipar).LowerLimit(), (*ipar).UpperLimit()); else if ((*ipar).HasLowerLimit() && !(*ipar).HasUpperLimit()) SetLowerLimit((*ipar).GetName(), (*ipar).LowerLimit()); else SetUpperLimit((*ipar).GetName(), (*ipar).UpperLimit()); } else { unsigned int i = trafo.IntOfExt((*ipar).Number()); double err = st.Error().IsValid() ? std::sqrt(2. * up * st.Error().InvHessian()(i, i)) : st.Parameters().Dirin()(i); Add((*ipar).GetName(), st.Vec()(i), err); } } // need to be set afterwards because becore the ::Add method set fCovarianceValid to false fCovarianceValid = st.Error().IsValid(); fCovStatus = -1; // when not available // if (st.Error().HesseFailed() || st.Error().InvertFailed() ) fCovStatus = -1; // when available if (st.Error().IsAvailable()) fCovStatus = 0; if (fCovarianceValid) { fCovariance = trafo.Int2extCovariance(st.Vec(), st.Error().InvHessian()); fIntCovariance = MnUserCovariance(std::vector(st.Error().InvHessian().Data(), st.Error().InvHessian().Data() + st.Error().InvHessian().size()), st.Error().InvHessian().Nrow()); fCovariance.Scale(2. * up); fGlobalCC = MnGlobalCorrelationCoeff(st.Error().InvHessian()); fGCCValid = fGlobalCC.IsValid(); assert(fCovariance.Nrow() == VariableParameters()); fCovStatus = 1; // when is valid } if (st.Error().IsMadePosDef()) fCovStatus = 2; if (st.Error().IsAccurate()) fCovStatus = 3; } MnUserCovariance MnUserParameterState::Hessian() const { // invert covariance matrix and return Hessian // need to copy in a MnSymMatrix MnPrint print("MnUserParameterState::Hessian"); MnAlgebraicSymMatrix mat(fCovariance.Nrow()); std::copy(fCovariance.Data().begin(), fCovariance.Data().end(), mat.Data()); int ifail = Invert(mat); if (ifail != 0) { print.Warn("Inversion failed; return diagonal matrix"); MnUserCovariance tmp(fCovariance.Nrow()); for (unsigned int i = 0; i < fCovariance.Nrow(); i++) { tmp(i, i) = 1. / fCovariance(i, i); } return tmp; } MnUserCovariance hessian(mat.Data(), fCovariance.Nrow()); return hessian; } // facade: forward interface of MnUserParameters and MnUserTransformation // via MnUserParameterState const std::vector &MnUserParameterState::MinuitParameters() const { // access to parameters (row-wise) return fParameters.Parameters(); } std::vector MnUserParameterState::Params() const { // access to parameters in column-wise representation return fParameters.Params(); } std::vector MnUserParameterState::Errors() const { // access to errors in column-wise representation return fParameters.Errors(); } const MinuitParameter &MnUserParameterState::Parameter(unsigned int i) const { // access to single Parameter i return fParameters.Parameter(i); } void MnUserParameterState::Add(const std::string &name, double val, double err) { MnPrint print("MnUserParameterState::Add"); // add free Parameter if (fParameters.Add(name, val, err)) { fIntParameters.push_back(val); fCovarianceValid = false; fGCCValid = false; fValid = true; } else { // redefine an existing parameter int i = Index(name); SetValue(i, val); if (Parameter(i).IsConst()) { print.Warn("Cannot modify status of constant parameter", name); return; } SetError(i, err); // release if it was fixed if (Parameter(i).IsFixed()) Release(i); } } void MnUserParameterState::Add(const std::string &name, double val, double err, double low, double up) { MnPrint print("MnUserParameterState::Add"); // add limited Parameter if (fParameters.Add(name, val, err, low, up)) { fCovarianceValid = false; fIntParameters.push_back(Ext2int(Index(name), val)); fGCCValid = false; fValid = true; } else { // Parameter already exist - just set values int i = Index(name); SetValue(i, val); if (Parameter(i).IsConst()) { print.Warn("Cannot modify status of constant parameter", name); return; } SetError(i, err); SetLimits(i, low, up); // release if it was fixed if (Parameter(i).IsFixed()) Release(i); } } void MnUserParameterState::Add(const std::string &name, double val) { // add const Parameter if (fParameters.Add(name, val)) fValid = true; else SetValue(name, val); } // interaction via external number of Parameter void MnUserParameterState::Fix(unsigned int e) { // fix parameter e (external index) if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) { unsigned int i = IntOfExt(e); if (fCovarianceValid) { fCovariance = MnCovarianceSqueeze()(fCovariance, i); fIntCovariance = MnCovarianceSqueeze()(fIntCovariance, i); } fIntParameters.erase(fIntParameters.begin() + i, fIntParameters.begin() + i + 1); } fParameters.Fix(e); fGCCValid = false; } void MnUserParameterState::Release(unsigned int e) { // release parameter e (external index) // no-op if parameter is const or if it is not fixed if (Parameter(e).IsConst() || !Parameter(e).IsFixed()) return; fParameters.Release(e); fCovarianceValid = false; fGCCValid = false; unsigned int i = IntOfExt(e); if (Parameter(e).HasLimits()) fIntParameters.insert(fIntParameters.begin() + i, Ext2int(e, Parameter(e).Value())); else fIntParameters.insert(fIntParameters.begin() + i, Parameter(e).Value()); } void MnUserParameterState::SetValue(unsigned int e, double val) { // set value for parameter e ( external index ) fParameters.SetValue(e, val); if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) { unsigned int i = IntOfExt(e); if (Parameter(e).HasLimits()) fIntParameters[i] = Ext2int(e, val); else fIntParameters[i] = val; } } void MnUserParameterState::SetError(unsigned int e, double val) { // set error for parameter e (external index) fParameters.SetError(e, val); } void MnUserParameterState::SetLimits(unsigned int e, double low, double up) { // set limits for parameter e (external index) fParameters.SetLimits(e, low, up); fCovarianceValid = false; fGCCValid = false; if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) { unsigned int i = IntOfExt(e); if (low < fIntParameters[i] && fIntParameters[i] < up) fIntParameters[i] = Ext2int(e, fIntParameters[i]); else if (low >= fIntParameters[i]) fIntParameters[i] = Ext2int(e, low + 0.1 * Parameter(e).Error()); else fIntParameters[i] = Ext2int(e, up - 0.1 * Parameter(e).Error()); } } void MnUserParameterState::SetUpperLimit(unsigned int e, double up) { // set upper limit for parameter e (external index) fParameters.SetUpperLimit(e, up); fCovarianceValid = false; fGCCValid = false; if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) { unsigned int i = IntOfExt(e); if (fIntParameters[i] < up) fIntParameters[i] = Ext2int(e, fIntParameters[i]); else fIntParameters[i] = Ext2int(e, up - 0.1 * Parameter(e).Error()); } } void MnUserParameterState::SetLowerLimit(unsigned int e, double low) { // set lower limit for parameter e (external index) fParameters.SetLowerLimit(e, low); fCovarianceValid = false; fGCCValid = false; if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) { unsigned int i = IntOfExt(e); if (low < fIntParameters[i]) fIntParameters[i] = Ext2int(e, fIntParameters[i]); else fIntParameters[i] = Ext2int(e, low + 0.1 * Parameter(e).Error()); } } void MnUserParameterState::RemoveLimits(unsigned int e) { // remove limit for parameter e (external index) fParameters.RemoveLimits(e); fCovarianceValid = false; fGCCValid = false; if (!Parameter(e).IsFixed() && !Parameter(e).IsConst()) fIntParameters[IntOfExt(e)] = Value(e); } double MnUserParameterState::Value(unsigned int i) const { // get value for parameter e (external index) return fParameters.Value(i); } double MnUserParameterState::Error(unsigned int i) const { // get error for parameter e (external index) return fParameters.Error(i); } // interaction via name of Parameter void MnUserParameterState::Fix(const std::string &name) { Fix(Index(name)); } void MnUserParameterState::Release(const std::string &name) { Release(Index(name)); } void MnUserParameterState::SetValue(const std::string &name, double val) { SetValue(Index(name), val); } void MnUserParameterState::SetError(const std::string &name, double val) { SetError(Index(name), val); } void MnUserParameterState::SetLimits(const std::string &name, double low, double up) { SetLimits(Index(name), low, up); } void MnUserParameterState::SetUpperLimit(const std::string &name, double up) { SetUpperLimit(Index(name), up); } void MnUserParameterState::SetLowerLimit(const std::string &name, double low) { SetLowerLimit(Index(name), low); } void MnUserParameterState::RemoveLimits(const std::string &name) { RemoveLimits(Index(name)); } double MnUserParameterState::Value(const std::string &name) const { return Value(Index(name)); } double MnUserParameterState::Error(const std::string &name) const { return Error(Index(name)); } unsigned int MnUserParameterState::Index(const std::string &name) const { // convert name into external number of Parameter return fParameters.Index(name); } const char *MnUserParameterState::Name(unsigned int i) const { // convert external number into name of Parameter (API returing a const char *) return fParameters.Name(i); } const std::string &MnUserParameterState::GetName(unsigned int i) const { // convert external number into name of Parameter (new interface returning a string) return fParameters.GetName(i); } // transformation internal <-> external (forward to transformation class) double MnUserParameterState::Int2ext(unsigned int i, double val) const { // internal to external value return fParameters.Trafo().Int2ext(i, val); } double MnUserParameterState::Ext2int(unsigned int e, double val) const { // external to internal value return fParameters.Trafo().Ext2int(e, val); } unsigned int MnUserParameterState::IntOfExt(unsigned int ext) const { // return internal index for external index ext return fParameters.Trafo().IntOfExt(ext); } unsigned int MnUserParameterState::ExtOfInt(unsigned int internal) const { // return external index for internal index internal return fParameters.Trafo().ExtOfInt(internal); } unsigned int MnUserParameterState::VariableParameters() const { // return number of variable parameters return fParameters.Trafo().VariableParameters(); } const MnMachinePrecision &MnUserParameterState::Precision() const { // return global parameter precision return fParameters.Precision(); } void MnUserParameterState::SetPrecision(double eps) { // set global parameter precision fParameters.SetPrecision(eps); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnUserParameters.cxx0000644000000000000000000001270714332717401021574 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnUserParameters.h" namespace ROOT { namespace Minuit2 { MnUserParameters::MnUserParameters(const std::vector &par, const std::vector &err) : fTransformation(par, err) { } // all implemented forwarding to MnUserTransformation class const std::vector &MnUserParameters::Parameters() const { // return vector of MinuitParameter objects return fTransformation.Parameters(); } std::vector MnUserParameters::Params() const { // return std::vector of double with parameter values return fTransformation.Params(); } std::vector MnUserParameters::Errors() const { // return std::vector of double with parameter errors return fTransformation.Errors(); } const MinuitParameter &MnUserParameters::Parameter(unsigned int n) const { // return the MinuitParameter object for index n (external) return fTransformation.Parameter(n); } bool MnUserParameters::Add(const std::string &name, double val, double err) { // add a new unlimited parameter giving name, value and err (step size) // return false if parameter already exists return fTransformation.Add(name, val, err); } bool MnUserParameters::Add(const std::string &name, double val, double err, double low, double up) { // add a new limited parameter giving name, value, err (step size) and lower/upper limits // return false if parameter already exists return fTransformation.Add(name, val, err, low, up); } bool MnUserParameters::Add(const std::string &name, double val) { // add a new unlimited parameter giving name and value // return false if parameter already exists return fTransformation.Add(name, val); } void MnUserParameters::Fix(unsigned int n) { // fix parameter n fTransformation.Fix(n); } void MnUserParameters::Release(unsigned int n) { // release parameter n fTransformation.Release(n); } void MnUserParameters::RemoveLimits(unsigned int n) { // remove limits for parameter n fTransformation.RemoveLimits(n); } void MnUserParameters::SetValue(unsigned int n, double val) { // set value for parameter n fTransformation.SetValue(n, val); } void MnUserParameters::SetError(unsigned int n, double err) { // set error for parameter n fTransformation.SetError(n, err); } void MnUserParameters::SetLimits(unsigned int n, double low, double up) { // set limits (lower/upper) for parameter n fTransformation.SetLimits(n, low, up); } void MnUserParameters::SetUpperLimit(unsigned int n, double up) { // set upper limit for parameter n fTransformation.SetUpperLimit(n, up); } void MnUserParameters::SetLowerLimit(unsigned int n, double low) { // set lower limit for parameter n fTransformation.SetLowerLimit(n, low); } void MnUserParameters::SetName(unsigned int n, const std::string &name) { // set name for parameter n fTransformation.SetName(n, name); } double MnUserParameters::Value(unsigned int n) const { // get value for parameter n return fTransformation.Value(n); } double MnUserParameters::Error(unsigned int n) const { // get error for parameter n return fTransformation.Error(n); } // interface using parameter name void MnUserParameters::Fix(const std::string &name) { // fix parameter Fix(Index(name)); } void MnUserParameters::Release(const std::string &name) { // release parameter Release(Index(name)); } void MnUserParameters::SetValue(const std::string &name, double val) { // set value for parameter SetValue(Index(name), val); } void MnUserParameters::SetError(const std::string &name, double err) { // set error SetError(Index(name), err); } void MnUserParameters::SetLimits(const std::string &name, double low, double up) { // set lower/upper limits SetLimits(Index(name), low, up); } void MnUserParameters::SetUpperLimit(const std::string &name, double up) { // set upper limit fTransformation.SetUpperLimit(Index(name), up); } void MnUserParameters::SetLowerLimit(const std::string &name, double low) { // set lower limit fTransformation.SetLowerLimit(Index(name), low); } void MnUserParameters::RemoveLimits(const std::string &name) { // remove limits RemoveLimits(Index(name)); } double MnUserParameters::Value(const std::string &name) const { // get parameter value return Value(Index(name)); } double MnUserParameters::Error(const std::string &name) const { // get parameter error return Error(Index(name)); } unsigned int MnUserParameters::Index(const std::string &name) const { // get index (external) corresponding to name return fTransformation.Index(name); } const std::string &MnUserParameters::GetName(unsigned int n) const { // get name corresponding to index (external) return fTransformation.GetName(n); } const char *MnUserParameters::Name(unsigned int n) const { // get name corresponding to index (external) return fTransformation.Name(n); } const MnMachinePrecision &MnUserParameters::Precision() const { // get global paramter precision return fTransformation.Precision(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/MnUserTransformation.cxx0000644000000000000000000004161214332717401022474 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnUserCovariance.h" #include "Minuit2/MnMatrix.h" #include #include #include #include namespace ROOT { namespace Minuit2 { class MnParStr { public: MnParStr(const std::string &name) : fName(name) {} ~MnParStr() {} bool operator()(const MinuitParameter &par) const { // return (strcmp(par.Name(), fName) == 0); return par.GetName() == fName; } private: const std::string &fName; }; MnUserTransformation::MnUserTransformation(const std::vector &par, const std::vector &err) : fPrecision(MnMachinePrecision()), fParameters(std::vector()), fExtOfInt(std::vector()), fDoubleLimTrafo(SinParameterTransformation()), fUpperLimTrafo(SqrtUpParameterTransformation()), fLowerLimTrafo(SqrtLowParameterTransformation()), fCache(std::vector()) { // constructor from a vector of parameter values and a vector of errors (step sizes) // class has as data member the transformation objects (all of the types), // the std::vector of MinuitParameter objects and the vector with the index conversions from // internal to external (fExtOfInt) fParameters.reserve(par.size()); fExtOfInt.reserve(par.size()); fCache.reserve(par.size()); std::string parName; for (unsigned int i = 0; i < par.size(); i++) { std::ostringstream buf; buf << "p" << i; parName = buf.str(); Add(parName, par[i], err[i]); } } //#ifdef MINUIT2_THREAD_SAFE // this if a thread-safe implementation needed if want to share transformation object between the threads std::vector MnUserTransformation::operator()(const MnAlgebraicVector &pstates) const { // transform an internal Minuit vector of internal values in a std::vector of external values // fixed parameters will have their fixed values unsigned int n = pstates.size(); // need to initialize to the stored (initial values) parameter values for the fixed ones std::vector pcache(fCache); for (unsigned int i = 0; i < n; i++) { if (fParameters[fExtOfInt[i]].HasLimits()) { pcache[fExtOfInt[i]] = Int2ext(i, pstates(i)); } else { pcache[fExtOfInt[i]] = pstates(i); } } return pcache; } // #else // const std::vector & MnUserTransformation::operator()(const MnAlgebraicVector& pstates) const { // // transform an internal Minuit vector of internal values in a std::vector of external values // // std::vector Cache(pstates.size() ); // for(unsigned int i = 0; i < pstates.size(); i++) { // if(fParameters[fExtOfInt[i]].HasLimits()) { // fCache[fExtOfInt[i]] = Int2ext(i, pstates(i)); // } else { // fCache[fExtOfInt[i]] = pstates(i); // } // } // return fCache; // } // #endif double MnUserTransformation::Int2ext(unsigned int i, double val) const { // return external value from internal value for parameter i if (fParameters[fExtOfInt[i]].HasLimits()) { if (fParameters[fExtOfInt[i]].HasUpperLimit() && fParameters[fExtOfInt[i]].HasLowerLimit()) return fDoubleLimTrafo.Int2ext(val, fParameters[fExtOfInt[i]].UpperLimit(), fParameters[fExtOfInt[i]].LowerLimit()); else if (fParameters[fExtOfInt[i]].HasUpperLimit() && !fParameters[fExtOfInt[i]].HasLowerLimit()) return fUpperLimTrafo.Int2ext(val, fParameters[fExtOfInt[i]].UpperLimit()); else return fLowerLimTrafo.Int2ext(val, fParameters[fExtOfInt[i]].LowerLimit()); } return val; } double MnUserTransformation::Int2extError(unsigned int i, double val, double err) const { // return external error from internal error for parameter i // err = sigma Value == std::sqrt(cov(i,i)) double dx = err; if (fParameters[fExtOfInt[i]].HasLimits()) { double ui = Int2ext(i, val); double du1 = Int2ext(i, val + dx) - ui; double du2 = Int2ext(i, val - dx) - ui; if (fParameters[fExtOfInt[i]].HasUpperLimit() && fParameters[fExtOfInt[i]].HasLowerLimit()) { // double al = fParameters[fExtOfInt[i]].Lower(); // double ba = fParameters[fExtOfInt[i]].Upper() - al; // double du1 = al + 0.5*(sin(val + dx) + 1.)*ba - ui; // double du2 = al + 0.5*(sin(val - dx) + 1.)*ba - ui; // if(dx > 1.) du1 = ba; if (dx > 1.) du1 = fParameters[fExtOfInt[i]].UpperLimit() - fParameters[fExtOfInt[i]].LowerLimit(); dx = 0.5 * (std::fabs(du1) + std::fabs(du2)); } else { dx = 0.5 * (std::fabs(du1) + std::fabs(du2)); } } return dx; } MnUserCovariance MnUserTransformation::Int2extCovariance(const MnAlgebraicVector &vec, const MnAlgebraicSymMatrix &cov) const { // return the external covariance matrix from the internal error matrix and the internal parameter value // the vector of internal parameter is needed for the derivatives (Jacobian of the transformation) // Vext(i,j) = Vint(i,j) * dPext(i)/dPint(i) * dPext(j)/dPint(j) MnUserCovariance result(cov.Nrow()); for (unsigned int i = 0; i < vec.size(); i++) { double dxdi = 1.; if (fParameters[fExtOfInt[i]].HasLimits()) { // dxdi = 0.5*std::fabs((fParameters[fExtOfInt[i]].Upper() - // fParameters[fExtOfInt[i]].Lower())*cos(vec(i))); dxdi = DInt2Ext(i, vec(i)); } for (unsigned int j = i; j < vec.size(); j++) { double dxdj = 1.; if (fParameters[fExtOfInt[j]].HasLimits()) { // dxdj = 0.5*std::fabs((fParameters[fExtOfInt[j]].Upper() - // fParameters[fExtOfInt[j]].Lower())*cos(vec(j))); dxdj = DInt2Ext(j, vec(j)); } result(i, j) = dxdi * cov(i, j) * dxdj; } // double diag = Int2extError(i, vec(i), std::sqrt(cov(i,i))); // result(i,i) = diag*diag; } return result; } double MnUserTransformation::Ext2int(unsigned int i, double val) const { // return the internal value for parameter i with external value val if (fParameters[i].HasLimits()) { if (fParameters[i].HasUpperLimit() && fParameters[i].HasLowerLimit()) return fDoubleLimTrafo.Ext2int(val, fParameters[i].UpperLimit(), fParameters[i].LowerLimit(), Precision()); else if (fParameters[i].HasUpperLimit() && !fParameters[i].HasLowerLimit()) return fUpperLimTrafo.Ext2int(val, fParameters[i].UpperLimit(), Precision()); else return fLowerLimTrafo.Ext2int(val, fParameters[i].LowerLimit(), Precision()); } return val; } double MnUserTransformation::DInt2Ext(unsigned int i, double val) const { // return the derivative of the int->ext transformation: dPext(i) / dPint(i) // for the parameter i with value val double dd = 1.; if (fParameters[fExtOfInt[i]].HasLimits()) { if (fParameters[fExtOfInt[i]].HasUpperLimit() && fParameters[fExtOfInt[i]].HasLowerLimit()) // dd = 0.5*std::fabs((fParameters[fExtOfInt[i]].Upper() - // fParameters[fExtOfInt[i]].Lower())*cos(vec(i))); dd = fDoubleLimTrafo.DInt2Ext(val, fParameters[fExtOfInt[i]].UpperLimit(), fParameters[fExtOfInt[i]].LowerLimit()); else if (fParameters[fExtOfInt[i]].HasUpperLimit() && !fParameters[fExtOfInt[i]].HasLowerLimit()) dd = fUpperLimTrafo.DInt2Ext(val, fParameters[fExtOfInt[i]].UpperLimit()); else dd = fLowerLimTrafo.DInt2Ext(val, fParameters[fExtOfInt[i]].LowerLimit()); } return dd; } /* double MnUserTransformation::dExt2Int(unsigned int, double) const { double dd = 1.; if(fParameters[fExtOfInt[i]].HasLimits()) { if(fParameters[fExtOfInt[i]].HasUpperLimit() && fParameters[fExtOfInt[i]].HasLowerLimit()) // dd = 0.5*std::fabs((fParameters[fExtOfInt[i]].Upper() - fParameters[fExtOfInt[i]].Lower())*cos(vec(i))); dd = fDoubleLimTrafo.dExt2Int(val, fParameters[fExtOfInt[i]].UpperLimit(), fParameters[fExtOfInt[i]].LowerLimit()); else if(fParameters[fExtOfInt[i]].HasUpperLimit() && !fParameters[fExtOfInt[i]].HasLowerLimit()) dd = fUpperLimTrafo.dExt2Int(val, fParameters[fExtOfInt[i]].UpperLimit()); else dd = fLowerLimTrafo.dExtInt(val, fParameters[fExtOfInt[i]].LowerLimit()); } return dd; } */ unsigned int MnUserTransformation::IntOfExt(unsigned int ext) const { // return internal index given external one ext assert(ext < fParameters.size()); assert(!fParameters[ext].IsFixed()); assert(!fParameters[ext].IsConst()); std::vector::const_iterator iind = std::find(fExtOfInt.begin(), fExtOfInt.end(), ext); assert(iind != fExtOfInt.end()); return (iind - fExtOfInt.begin()); } std::vector MnUserTransformation::Params() const { // return std::vector of double with parameter values unsigned int n = fParameters.size(); std::vector result(n); for (unsigned int i = 0; i < n; ++i) result[i] = fParameters[i].Value(); return result; } std::vector MnUserTransformation::Errors() const { // return std::vector of double with parameter errors std::vector result; result.reserve(fParameters.size()); for (std::vector::const_iterator ipar = Parameters().begin(); ipar != Parameters().end(); ++ipar) result.push_back((*ipar).Error()); return result; } const MinuitParameter &MnUserTransformation::Parameter(unsigned int n) const { // return the MinuitParameter object for index n (external) assert(n < fParameters.size()); return fParameters[n]; } // bool MnUserTransformation::Remove(const std::string & name) { // // remove parameter with name // // useful if want to re-define a parameter // // return false if parameter does not exist // std::vector::iterator itr = std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name) // ); if (itr == fParameters.end() ) return false; int n = itr - fParameters.begin(); if (n < 0 || n >= // fParameters.size() ) return false; fParameters.erase(itr); fCache.erase( fExtOfInt.begin() + n); // std::vector::iterator iind = std::find(fExtOfInt.begin(), fExtOfInt.end(), n); // if (iind != fExtOfInt.end()) fExtOfInt.erase(iind); // } bool MnUserTransformation::Add(const std::string &name, double val, double err) { // add a new unlimited parameter giving name, value and err (step size) // return false if parameter already exists if (std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name)) != fParameters.end()) return false; fExtOfInt.push_back(fParameters.size()); fCache.push_back(val); fParameters.push_back(MinuitParameter(fParameters.size(), name, val, err)); return true; } bool MnUserTransformation::Add(const std::string &name, double val, double err, double low, double up) { // add a new limited parameter giving name, value, err (step size) and lower/upper limits // return false if parameter already exists if (std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name)) != fParameters.end()) return false; fExtOfInt.push_back(fParameters.size()); fCache.push_back(val); fParameters.push_back(MinuitParameter(fParameters.size(), name, val, err, low, up)); return true; } bool MnUserTransformation::Add(const std::string &name, double val) { // add a new constant parameter giving name and value // return false if parameter already exists if (std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name)) != fParameters.end()) return false; fCache.push_back(val); // costant parameter - do not add in list of internals (fExtOfInt) fParameters.push_back(MinuitParameter(fParameters.size(), name, val)); return true; } void MnUserTransformation::Fix(unsigned int n) { // fix parameter n (external index) assert(n < fParameters.size()); std::vector::iterator iind = std::find(fExtOfInt.begin(), fExtOfInt.end(), n); if (iind != fExtOfInt.end()) fExtOfInt.erase(iind, iind + 1); fParameters[n].Fix(); } void MnUserTransformation::Release(unsigned int n) { // release parameter n (external index) assert(n < fParameters.size()); std::vector::const_iterator iind = std::find(fExtOfInt.begin(), fExtOfInt.end(), n); if (iind == fExtOfInt.end()) { fExtOfInt.push_back(n); std::sort(fExtOfInt.begin(), fExtOfInt.end()); } fParameters[n].Release(); } void MnUserTransformation::SetValue(unsigned int n, double val) { // set value for parameter n (external index) assert(n < fParameters.size()); fParameters[n].SetValue(val); fCache[n] = val; } void MnUserTransformation::SetError(unsigned int n, double err) { // set error for parameter n (external index) assert(n < fParameters.size()); fParameters[n].SetError(err); } void MnUserTransformation::SetLimits(unsigned int n, double low, double up) { // set limits (lower/upper) for parameter n (external index) assert(n < fParameters.size()); assert(low != up); fParameters[n].SetLimits(low, up); } void MnUserTransformation::SetUpperLimit(unsigned int n, double up) { // set upper limit for parameter n (external index) assert(n < fParameters.size()); fParameters[n].SetUpperLimit(up); } void MnUserTransformation::SetLowerLimit(unsigned int n, double lo) { // set lower limit for parameter n (external index) assert(n < fParameters.size()); fParameters[n].SetLowerLimit(lo); } void MnUserTransformation::RemoveLimits(unsigned int n) { // remove limits for parameter n (external index) assert(n < fParameters.size()); fParameters[n].RemoveLimits(); } void MnUserTransformation::SetName(unsigned int n, const std::string &name) { // set name for parameter n (external index) assert(n < fParameters.size()); fParameters[n].SetName(name); } double MnUserTransformation::Value(unsigned int n) const { // get value for parameter n (external index) assert(n < fParameters.size()); return fParameters[n].Value(); } double MnUserTransformation::Error(unsigned int n) const { // get error for parameter n (external index) assert(n < fParameters.size()); return fParameters[n].Error(); } // interface by parameter name void MnUserTransformation::Fix(const std::string &name) { // fix parameter Fix(Index(name)); } void MnUserTransformation::Release(const std::string &name) { // release parameter Release(Index(name)); } void MnUserTransformation::SetValue(const std::string &name, double val) { // set value for parameter SetValue(Index(name), val); } void MnUserTransformation::SetError(const std::string &name, double err) { // set error SetError(Index(name), err); } void MnUserTransformation::SetLimits(const std::string &name, double low, double up) { // set lower/upper limits SetLimits(Index(name), low, up); } void MnUserTransformation::SetUpperLimit(const std::string &name, double up) { // set upper limit SetUpperLimit(Index(name), up); } void MnUserTransformation::SetLowerLimit(const std::string &name, double lo) { // set lower limit SetLowerLimit(Index(name), lo); } void MnUserTransformation::RemoveLimits(const std::string &name) { // remove limits RemoveLimits(Index(name)); } double MnUserTransformation::Value(const std::string &name) const { // get parameter value return Value(Index(name)); } double MnUserTransformation::Error(const std::string &name) const { // get parameter error return Error(Index(name)); } unsigned int MnUserTransformation::Index(const std::string &name) const { // get index (external) corresponding to name std::vector::const_iterator ipar = std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name)); assert(ipar != fParameters.end()); // return (ipar - fParameters.begin()); return (*ipar).Number(); } int MnUserTransformation::FindIndex(const std::string &name) const { // find index (external) corresponding to name - return -1 if not found std::vector::const_iterator ipar = std::find_if(fParameters.begin(), fParameters.end(), MnParStr(name)); if (ipar == fParameters.end()) return -1; return (*ipar).Number(); } const std::string &MnUserTransformation::GetName(unsigned int n) const { // get name corresponding to index (external) assert(n < fParameters.size()); return fParameters[n].GetName(); } const char *MnUserTransformation::Name(unsigned int n) const { // get name corresponding to index (external) return GetName(n).c_str(); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/ModularFunctionMinimizer.cxx0000644000000000000000000002221014332717401023322 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/ModularFunctionMinimizer.h" #include "Minuit2/MinimumSeedGenerator.h" #include "Minuit2/AnalyticalGradientCalculator.h" #include "Minuit2/ExternalInternalGradientCalculator.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/MinimumBuilder.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnUserParameters.h" #include "Minuit2/MnUserCovariance.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnUserFcn.h" #include "Minuit2/FCNBase.h" #include "Minuit2/FCNGradientBase.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnHesse.h" #include "Minuit2/MnLineSearch.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { // #include "Minuit2/MnUserParametersPrint.h" FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra, unsigned int maxfcn, double toler) const { // minimize from FCNBase and std::vector of double's for parameter values and errors (step sizes) MnUserParameterState st(par, err); MnStrategy strategy(stra); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNGradientBase &fcn, const std::vector &par, const std::vector &err, unsigned int stra, unsigned int maxfcn, double toler) const { // minimize from FCNGradientBase (use analytical gradient provided in FCN) // and std::vector of double's for parameter values and errors (step sizes) MnUserParameterState st(par, err); MnStrategy strategy(stra); return Minimize(fcn, st, strategy, maxfcn, toler); } // move nrow before cov to avoid ambiguities when using default parameters FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra, unsigned int maxfcn, double toler) const { // minimize from FCNBase using std::vector for parameter error and // an std::vector of size n*(n+1)/2 for the covariance matrix and n (rank of cov matrix) MnUserParameterState st(par, cov, nrow); MnStrategy strategy(stra); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNGradientBase &fcn, const std::vector &par, unsigned int nrow, const std::vector &cov, unsigned int stra, unsigned int maxfcn, double toler) const { // minimize from FCNGradientBase (use analytical gradient provided in FCN) // using std::vector for parameter error and // an std::vector of size n*(n+1)/2 for the covariance matrix and n (rank of cov matrix) MnUserParameterState st(par, cov, nrow); MnStrategy strategy(stra); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNBase &fcn, const MnUserParameters &upar, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from FCNBase and MnUserParameters object MnUserParameterState st(upar); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNGradientBase &fcn, const MnUserParameters &upar, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from FCNGradientBase (use analytical gradient provided in FCN) and MnUserParameters object MnUserParameterState st(upar); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNBase &fcn, const MnUserParameters &upar, const MnUserCovariance &cov, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from FCNBase and MnUserParameters and MnUserCovariance objects MnUserParameterState st(upar, cov); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNGradientBase &fcn, const MnUserParameters &upar, const MnUserCovariance &cov, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from FCNGradientBase (use analytical gradient provided in FCN) and // MnUserParameters MnUserCovariance objects MnUserParameterState st(upar, cov); return Minimize(fcn, st, strategy, maxfcn, toler); } FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNBase &fcn, const MnUserParameterState &st, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from a FCNBase and a MnUserparameterState - interface used by all the previous ones // based on FCNBase. Create in this case a NumericalGradient calculator // Create the minuit FCN wrapper (MnUserFcn) containing the trasformation (int<->ext) // neeed MnUsserFcn for difference int-ext parameters MnUserFcn mfcn(fcn, st.Trafo()); Numerical2PGradientCalculator gc(mfcn, st.Trafo(), strategy); unsigned int npar = st.VariableParameters(); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; MinimumSeed mnseeds = SeedGenerator()(mfcn, gc, st, strategy); return Minimize(mfcn, gc, mnseeds, strategy, maxfcn, toler); } // use Gradient here FunctionMinimum ModularFunctionMinimizer::Minimize(const FCNGradientBase &fcn, const MnUserParameterState &st, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // minimize from a FCNGradientBase and a MnUserparameterState - interface used by all the previous ones // based on FCNGradientBase. // Create in this acase an AnalyticalGradient calculator // Create the minuit FCN wrapper (MnUserFcn) containing the trasformation (int<->ext) MnUserFcn mfcn(fcn, st.Trafo()); AnalyticalGradientCalculator *gc; if (fcn.gradParameterSpace() == GradientParameterSpace::Internal) { // std::cout << "-- ModularFunctionMinimizer::Minimize: Internal parameter space" << std::endl; gc = new ExternalInternalGradientCalculator(fcn, st.Trafo()); } else { // std::cout << "-- ModularFunctionMinimizer::Minimize: External parameter space" << std::endl; gc = new AnalyticalGradientCalculator(fcn, st.Trafo()); } unsigned int npar = st.VariableParameters(); if (maxfcn == 0) maxfcn = 200 + 100 * npar + 5 * npar * npar; // use numerical gradient to compute initial derivatives for SeedGenerator Numerical2PGradientCalculator numgc(mfcn, st.Trafo(), strategy); MinimumSeed mnseeds = SeedGenerator()(mfcn, numgc, st, strategy); auto minimum = Minimize(mfcn, *gc, mnseeds, strategy, maxfcn, toler); delete gc; return minimum; } FunctionMinimum ModularFunctionMinimizer::Minimize(const MnFcn &mfcn, const GradientCalculator &gc, const MinimumSeed &seed, const MnStrategy &strategy, unsigned int maxfcn, double toler) const { // Interface used by all the others for the minimization using the base MinimumBuilder class // According to the contained type of MinimumBuilder the right type will be used MnPrint print("ModularFunctionMinimizer"); const MinimumBuilder &mb = Builder(); // std::cout << typeid(&mb).Name() << std::endl; double effective_toler = toler * mfcn.Up(); // scale tolerance with Up() // avoid tolerance too smalls (than limits) double eps = MnMachinePrecision().Eps2(); if (effective_toler < eps) effective_toler = eps; // check if maxfcn is already exhausted // case already reached call limit if (mfcn.NumOfCalls() >= maxfcn) { print.Warn("Stop before iterating - call limit already exceeded"); return FunctionMinimum(seed, std::vector(1, seed.State()), mfcn.Up(), FunctionMinimum::MnReachedCallLimit); } return mb.Minimum(mfcn, gc, seed, strategy, maxfcn, effective_toler); } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/NegativeG2LineSearch.cxx0000644000000000000000000001026414332717401022224 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/NegativeG2LineSearch.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MinimumState.h" #include "Minuit2/GradientCalculator.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnLineSearch.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/VariableMetricEDMEstimator.h" #include "Minuit2/MnPrint.h" #include namespace ROOT { namespace Minuit2 { MinimumState NegativeG2LineSearch::operator()(const MnFcn &fcn, const MinimumState &st, const GradientCalculator &gc, const MnMachinePrecision &prec) const { // when the second derivatives are negative perform a line search along Parameter which gives // negative second derivative and magnitude equal to the Gradient step size. // Recalculate the gradients for all the Parameter after the correction and // continue iteration in case the second derivatives are still negative // MnPrint print("NegativeG2LineSearch"); bool negG2 = HasNegativeG2(st.Gradient(), prec); if (!negG2) return st; unsigned int n = st.Parameters().Vec().size(); FunctionGradient dgrad = st.Gradient(); MinimumParameters pa = st.Parameters(); bool iterate = false; unsigned int iter = 0; do { iterate = false; for (unsigned int i = 0; i < n; i++) { print.Debug("negative G2 - iter", iter, "param", i, pa.Vec()(i), "grad2", dgrad.G2()(i), "grad", dgrad.Vec()(i), "grad step", dgrad.Gstep()(i), "step size", pa.Dirin()(i)); if (dgrad.G2()(i) <= 0) { // check also the gradient (if it is zero ) I can skip the param) if (std::fabs(dgrad.Vec()(i)) < prec.Eps() && std::fabs(dgrad.G2()(i)) < prec.Eps()) continue; // if(dgrad.G2()(i) < prec.Eps()) { // do line search if second derivative negative MnAlgebraicVector step(n); MnLineSearch lsearch; if (dgrad.Vec()(i) < 0) step(i) = dgrad.Gstep()(i); //*dgrad.Vec()(i); else step(i) = -dgrad.Gstep()(i); // *dgrad.Vec()(i); double gdel = step(i) * dgrad.Vec()(i); // if using sec derivative information // double g2del = step(i)*step(i) * dgrad.G2()(i); print.Debug("step(i)", step(i), "gdel", gdel); // std::cout << " g2del " << g2del << std::endl; MnParabolaPoint pp = lsearch(fcn, pa, step, gdel, prec); print.Debug("Line search result", pp.X(), "f(0)", pa.Fval(), "f(1)", pp.Y()); step *= pp.X(); pa = MinimumParameters(pa.Vec() + step, pp.Y()); dgrad = gc(pa, dgrad); print.Debug("Line search - iter", iter, "param", i, pa.Vec()(i), "step", step(i), "new grad2", dgrad.G2()(i), "new grad", dgrad.Vec()(i), "grad step", dgrad.Gstep()(i)); iterate = true; break; } } } while (iter++ < 2 * n && iterate); MnAlgebraicSymMatrix mat(n); for (unsigned int i = 0; i < n; i++) mat(i, i) = (std::fabs(dgrad.G2()(i)) > prec.Eps2() ? 1. / dgrad.G2()(i) : 1.); MinimumError err(mat, 1.); double edm = VariableMetricEDMEstimator().Estimate(dgrad, err); if (edm < 0) { err = MinimumError(mat, MinimumError::MnNotPosDef); } return MinimumState(pa, err, dgrad, edm, fcn.NumOfCalls()); } bool NegativeG2LineSearch::HasNegativeG2(const FunctionGradient &grad, const MnMachinePrecision & /*prec */) const { // check if function gradient has any component which is neegative for (unsigned int i = 0; i < grad.Vec().size(); i++) if (grad.G2()(i) <= 0) { return true; } return false; } } // namespace Minuit2 } // namespace ROOT iminuit-2.24.0/extern/root/math/minuit2/src/Numerical2PGradientCalculator.cxx0000644000000000000000000002065114332717401024145 0ustar00// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnUserTransformation.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MinimumParameters.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnPrint.h" #ifdef _OPENMP #include #endif #include #include #include #include "Minuit2/MPIProcess.h" namespace ROOT { namespace Minuit2 { FunctionGradient Numerical2PGradientCalculator::operator()(const MinimumParameters &par) const { // calculate gradient using Initial gradient calculator and from MinimumParameters object InitialGradientCalculator gc(fFcn, fTransformation, fStrategy); FunctionGradient gra = gc(par); return (*this)(par, gra); } // comment it, because it was added FunctionGradient Numerical2PGradientCalculator::operator()(const std::vector ¶ms) const { // calculate gradient from an std;:vector of paramteters int npar = params.size(); MnAlgebraicVector par(npar); for (int i = 0; i < npar; ++i) { par(i) = params[i]; } double fval = Fcn()(par); MinimumParameters minpars = MinimumParameters(par, fval); return (*this)(minpars); } FunctionGradient Numerical2PGradientCalculator:: operator()(const MinimumParameters &par, const FunctionGradient &Gradient) const { // calculate numerical gradient from MinimumParameters object // the algorithm takes correctly care when the gradient is approximatly zero // std::cout<<"########### Numerical2PDerivative"< 0.5) step = 0.5; } double stpmax = 10. * std::fabs(gstep(i)); if (step > stpmax) step = stpmax; // std::cout<<" "< #include #include #include #include #include #include "Fit/ParameterSettings.h" #include // needed here because in Fitter is only a forward declaration namespace ROOT { namespace Minuit2 { NumericalDerivator::NumericalDerivator(bool always_exactly_mimic_minuit2) : fAlwaysExactlyMimicMinuit2(always_exactly_mimic_minuit2) { } NumericalDerivator::NumericalDerivator(double step_tolerance, double grad_tolerance, unsigned int ncycles, double error_level, bool always_exactly_mimic_minuit2) : fStepTolerance(step_tolerance), fGradTolerance(grad_tolerance), fUp(error_level), fNCycles(ncycles), fAlwaysExactlyMimicMinuit2(always_exactly_mimic_minuit2) { } NumericalDerivator::NumericalDerivator(const NumericalDerivator &/*other*/) = default; /// This function sets internal state based on input parameters. This state /// setup is used in the actual (partial) derivative calculations. void NumericalDerivator::SetupDifferentiate(const ROOT::Math::IBaseFunctionMultiDim *function, const double *cx, const std::vector ¶meters) { assert(function != nullptr && "function is a nullptr"); fVx.resize(function->NDim()); fVxExternal.resize(function->NDim()); fVxFValCache.resize(function->NDim()); std::copy(cx, cx + function->NDim(), fVx.data()); // convert to Minuit external parameters for (unsigned i = 0; i < function->NDim(); i++) { fVxExternal[i] = Int2ext(parameters[i], fVx[i]); } if (fVx != fVxFValCache) { fVxFValCache = fVx; fVal = (*function)(fVxExternal.data()); // value of function at given points } fDfmin = 8. * fPrecision.Eps2() * (std::abs(fVal) + fUp); fVrysml = 8. * fPrecision.Eps() * fPrecision.Eps(); } DerivatorElement NumericalDerivator::PartialDerivative(const ROOT::Math::IBaseFunctionMultiDim *function, const double *x, const std::vector ¶meters, unsigned int i_component, DerivatorElement previous) { SetupDifferentiate(function, x, parameters); return FastPartialDerivative(function, parameters, i_component, previous); } // leaves the parameter setup to the caller DerivatorElement NumericalDerivator::FastPartialDerivative(const ROOT::Math::IBaseFunctionMultiDim *function, const std::vector ¶meters, unsigned int i_component, const DerivatorElement &previous) { DerivatorElement deriv{previous}; double xtf = fVx[i_component]; double epspri = fPrecision.Eps2() + std::abs(deriv.derivative * fPrecision.Eps2()); double step_old = 0.; for (unsigned int j = 0; j < fNCycles; ++j) { double optstp = std::sqrt(fDfmin / (std::abs(deriv.second_derivative) + epspri)); double step = std::max(optstp, std::abs(0.1 * deriv.step_size)); if (parameters[i_component].IsBound()) { if (step > 0.5) step = 0.5; } double stpmax = 10. * std::abs(deriv.step_size); if (step > stpmax) step = stpmax; double stpmin = std::max(fVrysml, 8. * std::abs(fPrecision.Eps2() * fVx[i_component])); if (step < stpmin) step = stpmin; if (std::abs((step - step_old) / step) < fStepTolerance) { break; } deriv.step_size = step; step_old = step; fVx[i_component] = xtf + step; fVxExternal[i_component] = Int2ext(parameters[i_component], fVx[i_component]); double fs1 = (*function)(fVxExternal.data()); fVx[i_component] = xtf - step; fVxExternal[i_component] = Int2ext(parameters[i_component], fVx[i_component]); double fs2 = (*function)(fVxExternal.data()); fVx[i_component] = xtf; fVxExternal[i_component] = Int2ext(parameters[i_component], fVx[i_component]); double fGrd_old = deriv.derivative; deriv.derivative = 0.5 * (fs1 - fs2) / step; deriv.second_derivative = (fs1 + fs2 - 2. * fVal) / step / step; if (std::abs(fGrd_old - deriv.derivative) / (std::abs(deriv.derivative) + fDfmin / step) < fGradTolerance) { break; } } return deriv; } DerivatorElement NumericalDerivator::operator()(const ROOT::Math::IBaseFunctionMultiDim *function, const double *x, const std::vector ¶meters, unsigned int i_component, const DerivatorElement &previous) { return PartialDerivative(function, x, parameters, i_component, previous); } std::vector NumericalDerivator::Differentiate(const ROOT::Math::IBaseFunctionMultiDim *function, const double *cx, const std::vector ¶meters, const std::vector &previous_gradient) { SetupDifferentiate(function, cx, parameters); std::vector gradient; gradient.reserve(function->NDim()); for (unsigned int ix = 0; ix < function->NDim(); ++ix) { gradient.emplace_back(FastPartialDerivative(function, parameters, ix, previous_gradient[ix])); } return gradient; } double NumericalDerivator::Int2ext(const ROOT::Fit::ParameterSettings ¶meter, double val) const { // return external value from internal value for parameter i if (parameter.IsBound()) { if (parameter.IsDoubleBound()) { return fDoubleLimTrafo.Int2ext(val, parameter.UpperLimit(), parameter.LowerLimit()); } else if (parameter.HasUpperLimit() && !parameter.HasLowerLimit()) { return fUpperLimTrafo.Int2ext(val, parameter.UpperLimit()); } else { return fLowerLimTrafo.Int2ext(val, parameter.LowerLimit()); } } return val; } double NumericalDerivator::Ext2int(const ROOT::Fit::ParameterSettings ¶meter, double val) const { // return the internal value for parameter i with external value val if (parameter.IsBound()) { if (parameter.IsDoubleBound()) { return fDoubleLimTrafo.Ext2int(val, parameter.UpperLimit(), parameter.LowerLimit(), fPrecision); } else if (parameter.HasUpperLimit() && !parameter.HasLowerLimit()) { return fUpperLimTrafo.Ext2int(val, parameter.UpperLimit(), fPrecision); } else { return fLowerLimTrafo.Ext2int(val, parameter.LowerLimit(), fPrecision); } } return val; } double NumericalDerivator::DInt2Ext(const ROOT::Fit::ParameterSettings ¶meter, double val) const { // return the derivative of the int->ext transformation: dPext(i) / dPint(i) // for the parameter i with value val double dd = 1.; if (parameter.IsBound()) { if (parameter.IsDoubleBound()) { dd = fDoubleLimTrafo.DInt2Ext(val, parameter.UpperLimit(), parameter.LowerLimit()); } else if (parameter.HasUpperLimit() && !parameter.HasLowerLimit()) { dd = fUpperLimTrafo.DInt2Ext(val, parameter.UpperLimit()); } else { dd = fLowerLimTrafo.DInt2Ext(val, parameter.LowerLimit()); } } return dd; } // MODIFIED: /// This function was not implemented as in Minuit2. Now it copies the behavior /// of InitialGradientCalculator. See https://github.com/roofit-dev/root/issues/10 void NumericalDerivator::SetInitialGradient(const ROOT::Math::IBaseFunctionMultiDim *, const std::vector ¶meters, std::vector &gradient) { // set an initial gradient using some given steps // (used in the first iteration) double eps2 = fPrecision.Eps2(); unsigned ix = 0; for (auto parameter = parameters.begin(); parameter != parameters.end(); ++parameter, ++ix) { // What Minuit2 calls "Error" is stepsize on the ROOT side. double werr = parameter->StepSize(); // Actually, sav in Minuit2 is the external parameter value, so that is // what we called var before and var is unnecessary here. double sav = parameter->Value(); // However, we do need var below, so let's calculate it using Ext2int: double var = Ext2int(*parameter, sav); if (fAlwaysExactlyMimicMinuit2) { // this transformation can lose a few bits, but Minuit2 does it too sav = Int2ext(*parameter, var); } double sav2 = sav + werr; if (parameter->HasUpperLimit() && sav2 > parameter->UpperLimit()) { sav2 = parameter->UpperLimit(); } double var2 = Ext2int(*parameter, sav2); double vplu = var2 - var; sav2 = sav - werr; if (parameter->HasLowerLimit() && sav2 < parameter->LowerLimit()) { sav2 = parameter->LowerLimit(); } var2 = Ext2int(*parameter, sav2); double vmin = var2 - var; double gsmin = 8. * eps2 * (std::abs(var) + eps2); // protect against very small step sizes which can cause dirin to zero and then nan values in grd double dirin = std::max(0.5 * (std::abs(vplu) + std::abs(vmin)), gsmin); double g2 = 2.0 * fUp / (dirin * dirin); double gstep = std::max(gsmin, 0.1 * dirin); double grd = g2 * dirin; if (parameter->IsBound()) { gstep = std::min(gstep, 0.5); } gradient[ix].derivative = grd; gradient[ix].second_derivative = g2; gradient[ix].step_size = gstep; } } std::ostream &operator<<(std::ostream &out, const DerivatorElement &value) { return out << "(derivative: " << value.derivative << ", second_derivative: " << value.second_derivative << ", step_size: " << value.step_size << ")"; } } // namespace Minuit2 } // namespace ROOT ��������iminuit-2.24.0/extern/root/math/minuit2/src/ParametricFunction.cxx����������������������������������0000644�0000000�0000000�00000003072�14332717401�022127� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/ParametricFunction.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/Numerical2PGradientCalculator.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/MnVectorTransform.h" namespace ROOT { namespace Minuit2 { //#include "Minuit2/MnPrint.h" std::vector ParametricFunction::GetGradient(const std::vector &x) const { // calculate the numerical gradient (using Numerical2PGradientCalculator) // LM: this I believe is not very efficient MnFcn mfcn(*this); MnStrategy strategy(1); // ????????? how does it know the transformation???????? std::vector err(x.size()); err.assign(x.size(), 0.1); // need to use parameters MnUserParameterState st(x, err); Numerical2PGradientCalculator gc(mfcn, st.Trafo(), strategy); FunctionGradient g = gc(x); const MnAlgebraicVector &grad = g.Vec(); assert(grad.size() == x.size()); MnVectorTransform vt; // std::cout << "Param Function Gradient " << grad << std::endl; return vt(grad); } } // namespace Minuit2 } // namespace ROOT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/ScanBuilder.cxx�����������������������������������������0000644�0000000�0000000�00000003541�14332717401�020526� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/ScanBuilder.h" #include "Minuit2/MnParameterScan.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnFcn.h" #include namespace ROOT { namespace Minuit2 { FunctionMinimum ScanBuilder::Minimum(const MnFcn &mfcn, const GradientCalculator &, const MinimumSeed &seed, const MnStrategy &, unsigned int, double) const { // find the function minimum performing a parameter scan (using MnParameterScan class) // function gradient is not used MnAlgebraicVector x = seed.Parameters().Vec(); MnUserParameterState upst(seed.State(), mfcn.Up(), seed.Trafo()); MnParameterScan scan(mfcn.Fcn(), upst.Parameters(), seed.Fval()); double amin = scan.Fval(); unsigned int n = seed.Trafo().VariableParameters(); MnAlgebraicVector dirin(n); for (unsigned int i = 0; i < n; i++) { unsigned int ext = seed.Trafo().ExtOfInt(i); scan(ext); if (scan.Fval() < amin) { amin = scan.Fval(); x(i) = seed.Trafo().Ext2int(ext, scan.Parameters().Value(ext)); } dirin(i) = std::sqrt(2. * mfcn.Up() * seed.Error().InvHessian()(i, i)); } MinimumParameters mp(x, dirin, amin); MinimumState st(mp, 0., mfcn.NumOfCalls()); return FunctionMinimum(seed, std::vector(1, st), mfcn.Up()); } } // namespace Minuit2 } // namespace ROOT ���������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/SimplexBuilder.cxx��������������������������������������0000644�0000000�0000000�00000017312�14332717401�021264� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/SimplexBuilder.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/SimplexParameters.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { class GradientCalculator; class MnStrategy; FunctionMinimum SimplexBuilder::Minimum(const MnFcn &mfcn, const GradientCalculator &, const MinimumSeed &seed, const MnStrategy &, unsigned int maxfcn, double minedm) const { // find the minimum using the Simplex method of Nelder and Mead (does not use function gradient) // method to find initial simplex is slightly different than in the orginal Fortran // Minuit since has not been proofed that one to be better // synchronize print levels MnPrint print("SimplexBuilder", PrintLevel()); print.Debug("Running with maxfcn", maxfcn, "minedm", minedm); const MnMachinePrecision &prec = seed.Precision(); MnAlgebraicVector x = seed.Parameters().Vec(); MnAlgebraicVector step = 10. * seed.Gradient().Gstep(); unsigned int n = x.size(); double wg = 1. / double(n); double alpha = 1., beta = 0.5, gamma = 2., rhomin = 4., rhomax = 8.; double rho1 = 1. + alpha; // double rho2 = rho1 + alpha*gamma; // change proposed by david sachs (fnal) double rho2 = 1. + alpha * gamma; std::vector> simpl; simpl.reserve(n + 1); simpl.push_back(std::pair(seed.Fval(), x)); unsigned int jl = 0, jh = 0; double amin = seed.Fval(), aming = seed.Fval(); for (unsigned int i = 0; i < n; i++) { double dmin = 8. * prec.Eps2() * (std::fabs(x(i)) + prec.Eps2()); if (step(i) < dmin) step(i) = dmin; x(i) += step(i); double tmp = mfcn(x); if (tmp < amin) { amin = tmp; jl = i + 1; } if (tmp > aming) { aming = tmp; jh = i + 1; } simpl.push_back(std::pair(tmp, x)); x(i) -= step(i); } SimplexParameters simplex(simpl, jh, jl); print.Debug([&](std::ostream &os) { os << "Initial parameters - min " << jl << " " << amin << " max " << jh << " " << aming << '\n'; for (unsigned int i = 0; i < simplex.Simplex().size(); ++i) os << " i = " << i << " x = " << simplex(i).second << " fval(x) = " << simplex(i).first << '\n'; }); double edmPrev = simplex.Edm(); int niterations = 0; do { jl = simplex.Jl(); jh = simplex.Jh(); amin = simplex(jl).first; edmPrev = simplex.Edm(); print.Debug("iteration: edm =", simplex.Edm(), '\n', "--> Min Param is", jl, "pmin", simplex(jl).second, "f(pmin)", amin, '\n', "--> Max param is", jh, simplex(jh).first); // std::cout << "ALL SIMPLEX PARAMETERS: "<< std::endl; // for (unsigned int i = 0; i < simplex.Simplex().size(); ++i) // std::cout << " i = " << i << " x = " << simplex(i).second << " fval(x) = " << simplex(i).first << // std::endl; // trace the iterations (need to create a MinimunState although errors and gradient are not existing) if (TraceIter()) TraceIteration(niterations, MinimumState(MinimumParameters(simplex(jl).second, simplex(jl).first), simplex.Edm(), mfcn.NumOfCalls())); print.Info(MnPrint::Oneline(simplex(jl).first, simplex.Edm(), mfcn.NumOfCalls(), niterations)); niterations++; MnAlgebraicVector pbar(n); for (unsigned int i = 0; i < n + 1; i++) { if (i == jh) continue; pbar += (wg * simplex(i).second); } MnAlgebraicVector pstar = (1. + alpha) * pbar - alpha * simplex(jh).second; double ystar = mfcn(pstar); print.Debug("pbar", pbar, "pstar", pstar, "f(pstar)", ystar); if (ystar > amin) { if (ystar < simplex(jh).first) { simplex.Update(ystar, pstar); if (jh != simplex.Jh()) continue; } MnAlgebraicVector pstst = beta * simplex(jh).second + (1. - beta) * pbar; double ystst = mfcn(pstst); print.Debug("Reduced simplex pstst", pstst, "f(pstst)", ystst); if (ystst > simplex(jh).first) break; simplex.Update(ystst, pstst); continue; } MnAlgebraicVector pstst = gamma * pstar + (1. - gamma) * pbar; double ystst = mfcn(pstst); print.Debug("pstst", pstst, "f(pstst)", ystst); double y1 = (ystar - simplex(jh).first) * rho2; double y2 = (ystst - simplex(jh).first) * rho1; double rho = 0.5 * (rho2 * y1 - rho1 * y2) / (y1 - y2); if (rho < rhomin) { if (ystst < simplex(jl).first) simplex.Update(ystst, pstst); else simplex.Update(ystar, pstar); continue; } if (rho > rhomax) rho = rhomax; MnAlgebraicVector prho = rho * pbar + (1. - rho) * simplex(jh).second; double yrho = mfcn(prho); print.Debug("prho", prho, "f(prho)", yrho); if (yrho < simplex(jl).first && yrho < ystst) { simplex.Update(yrho, prho); continue; } if (ystst < simplex(jl).first) { simplex.Update(ystst, pstst); continue; } if (yrho > simplex(jl).first) { if (ystst < simplex(jl).first) simplex.Update(ystst, pstst); else simplex.Update(ystar, pstar); continue; } if (ystar > simplex(jh).first) { pstst = beta * simplex(jh).second + (1. - beta) * pbar; ystst = mfcn(pstst); if (ystst > simplex(jh).first) break; simplex.Update(ystst, pstst); } print.Debug("End loop : Edm", simplex.Edm(), "pstst", pstst, "f(pstst)", ystst); } while ((simplex.Edm() > minedm || edmPrev > minedm) && mfcn.NumOfCalls() < maxfcn); jl = simplex.Jl(); jh = simplex.Jh(); amin = simplex(jl).first; MnAlgebraicVector pbar(n); for (unsigned int i = 0; i < n + 1; i++) { if (i == jh) continue; pbar += (wg * simplex(i).second); } double ybar = mfcn(pbar); if (ybar < amin) simplex.Update(ybar, pbar); else { pbar = simplex(jl).second; ybar = simplex(jl).first; } MnAlgebraicVector dirin = simplex.Dirin(); // Scale to sigmas on parameters werr^2 = dirin^2 * (up/edm) dirin *= std::sqrt(mfcn.Up() / simplex.Edm()); print.Debug("End simplex edm =", simplex.Edm(), "pbar =", pbar, "f(p) =", ybar); MinimumState st(MinimumParameters(pbar, dirin, ybar), simplex.Edm(), mfcn.NumOfCalls()); print.Info("Final iteration", MnPrint::Oneline(st)); if (TraceIter()) TraceIteration(niterations, st); if (mfcn.NumOfCalls() > maxfcn) { print.Warn("Simplex did not converge, #fcn calls exhausted"); return FunctionMinimum(seed, std::vector(1, st), mfcn.Up(), FunctionMinimum::MnReachedCallLimit); } if (simplex.Edm() > minedm) { print.Warn("Simplex did not converge, edm > minedm"); return FunctionMinimum(seed, std::vector(1, st), mfcn.Up(), FunctionMinimum::MnAboveMaxEdm); } return FunctionMinimum(seed, std::vector(1, st), mfcn.Up()); } } // namespace Minuit2 } // namespace ROOT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/SimplexParameters.cxx�����������������������������������0000644�0000000�0000000�00000003307�14332717401�022000� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/SimplexParameters.h" namespace ROOT { namespace Minuit2 { void SimplexParameters::Update(double y, const MnAlgebraicVector &p) { // update the SimplexParameter object with a new value y = FCN(p) fSimplexParameters[Jh()] = std::pair(y, p); if (y < fSimplexParameters[Jl()].first) fJLow = Jh(); unsigned int jh = 0; for (unsigned int i = 1; i < fSimplexParameters.size(); i++) { if (fSimplexParameters[i].first > fSimplexParameters[jh].first) jh = i; } fJHigh = jh; return; } MnAlgebraicVector SimplexParameters::Dirin() const { // find simplex direction (vector from big to smaller parameter points) MnAlgebraicVector dirin(fSimplexParameters.size() - 1); for (unsigned int i = 0; i < fSimplexParameters.size() - 1; i++) { double pbig = fSimplexParameters[0].second(i), plit = pbig; for (unsigned int j = 0; j < fSimplexParameters.size(); j++) { if (fSimplexParameters[j].second(i) < plit) plit = fSimplexParameters[j].second(i); if (fSimplexParameters[j].second(i) > pbig) pbig = fSimplexParameters[j].second(i); } dirin(i) = pbig - plit; } return dirin; } } // namespace Minuit2 } // namespace ROOT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/SimplexSeedGenerator.cxx��������������������������������0000644�0000000�0000000�00000004272�14332717401�022426� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/SimplexSeedGenerator.h" #include "Minuit2/MnUserParameterState.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/InitialGradientCalculator.h" #include "Minuit2/VariableMetricEDMEstimator.h" namespace ROOT { namespace Minuit2 { MinimumSeed SimplexSeedGenerator:: operator()(const MnFcn &fcn, const GradientCalculator &, const MnUserParameterState &st, const MnStrategy &stra) const { // create starting state for Simplex, which corresponds to the initial parameter values // using the simple Initial gradient calculator (does not use any FCN function calls) unsigned int n = st.VariableParameters(); const MnMachinePrecision &prec = st.Precision(); // initial starting values MnAlgebraicVector x(n); for (unsigned int i = 0; i < n; i++) x(i) = st.IntParameters()[i]; double fcnmin = fcn(x); MinimumParameters pa(x, fcnmin); InitialGradientCalculator igc(fcn, st.Trafo(), stra); FunctionGradient dgrad = igc(pa); MnAlgebraicSymMatrix mat(n); double dcovar = 1.; for (unsigned int i = 0; i < n; i++) mat(i, i) = (std::fabs(dgrad.G2()(i)) > prec.Eps2() ? 1. / dgrad.G2()(i) : 1.); MinimumError err(mat, dcovar); double edm = VariableMetricEDMEstimator().Estimate(dgrad, err); MinimumState state(pa, err, dgrad, edm, fcn.NumOfCalls()); return MinimumSeed(state, st.Trafo()); } MinimumSeed SimplexSeedGenerator::operator()(const MnFcn &fcn, const AnalyticalGradientCalculator &gc, const MnUserParameterState &st, const MnStrategy &stra) const { // base class interface return (*this)(fcn, (const GradientCalculator &)(gc), st, stra); } } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/SinParameterTransformation.cxx��������������������������0000644�0000000�0000000�00000004166�14332717401�023660� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei, E.G.P. Bos 2003-2017 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/SinParameterTransformation.h" #include "Minuit2/MnMachinePrecision.h" #include namespace ROOT { namespace Minuit2 { long double SinParameterTransformation::Int2ext(long double Value, long double Upper, long double Lower) const { // transformation from to internal (unlimited) to external values (limited by Lower/Upper ) return Lower + 0.5 * (Upper - Lower) * (std::sin(Value) + 1.); } long double SinParameterTransformation::Ext2int(long double Value, long double Upper, long double Lower, const MnMachinePrecision &prec) const { // transformation from external (limited by Lower/Upper ) to internal (unlimited) values given the lower/upper // limits long double piby2 = 2. * std::atan(1.); long double distnn = 8. * std::sqrt(prec.Eps2()); long double vlimhi = piby2 - distnn; long double vlimlo = -piby2 + distnn; long double yy = 2. * (Value - Lower) / (Upper - Lower) - 1.; long double yy2 = yy * yy; if (yy2 > (1. - prec.Eps2())) { if (yy < 0.) { // Lower limit // std::cout<<"SinParameterTransformation warning: is at its Lower allowed limit. "<GetEntries() + 0.5); fHistoFval->GetXaxis()->SetRange(1, niter); } if (fHistoEdm) fHistoEdm->GetXaxis()->SetRange(1, niter); if (fHistoParList) { for (int i = 0; i < fHistoParList->GetSize(); ++i) { TH1 *h1 = (TH1 *)fHistoParList->At(i); if (h1) h1->GetXaxis()->SetRange(1, niter); } } } void TMinuit2TraceObject::Init(const ROOT::Minuit2::MnUserParameterState &state) { ROOT::Minuit2::MnTraceObject::Init(state); fIterOffset = 0; // build debug histogram if (fHistoFval) delete fHistoFval; if (fHistoEdm) delete fHistoEdm; if (fHistoParList) { fHistoParList->Delete(); delete fHistoParList; } if (fMinuitPad) delete fMinuitPad; fHistoFval = new TH1D("minuit2_hist_fval", "Function Value/iteration", 2, 0, 1); fHistoEdm = new TH1D("minuit2_hist_edm", "Edm/iteration", 2, 0, 1); fHistoFval->SetCanExtend(TH1::kAllAxes); fHistoEdm->SetCanExtend(TH1::kAllAxes); // create histos for all parameters fHistoParList = new TList(); for (unsigned int ipar = 0; ipar < state.Params().size(); ++ipar) { if (state.Parameter(ipar).IsFixed() || state.Parameter(ipar).IsConst()) continue; TH1D *h1 = new TH1D(TString::Format("minuit2_hist_par%d", ipar), TString::Format("Value of %s/iteration", state.Name(ipar)), 2, 0, 1); h1->SetCanExtend(TH1::kAllAxes); fHistoParList->Add(h1); } if (gPad) fOldPad = gPad; // fMinuitPad = new TCanvas("c1_minuit2","TMinuit2 Progress",2); // fMinuitPad->Divide(1,3); // fMinuitPad->cd(1); fHistoFval->Draw(); // fMinuitPad->cd(2); fHistoEdm->Draw(); // fMinuitPad->cd(3); fHistoPar->Draw(); fHistoFval->Draw("hist"); fMinuitPad = gPad; } void TMinuit2TraceObject::operator()(int iter, const ROOT::Minuit2::MinimumState &state) { // action for each iteration: fill histograms // if iteration number is < 0 add at the end of current histograms // if offset is > 0 start filling from end of previous histogram int lastIter = int(fHistoFval->GetEntries() + 0.5); if (iter < 0) iter = lastIter; else { if (iter == 0 && lastIter > 0) fIterOffset = lastIter; iter += fIterOffset; } ROOT::Minuit2::MnTraceObject::operator()(iter, state); fHistoFval->SetBinContent(iter + 1, state.Fval()); fHistoEdm->SetBinContent(iter + 1, state.Edm()); for (unsigned int ipar = 0; ipar < state.Vec().size(); ++ipar) { double eval = UserState().Trafo().Int2ext(ipar, state.Vec()(ipar)); TH1 *histoPar = (TH1 *)fHistoParList->At(ipar); histoPar->SetBinContent(iter + 1, eval); } if (fMinuitPad) { if (ParNumber() == -2) fHistoEdm->Draw(); else if (ParNumber() >= 0 && ParNumber() < fHistoParList->GetSize()) { fHistoParList->At(ParNumber())->Draw(); } else fHistoFval->Draw(); } } ������������������������iminuit-2.24.0/extern/root/math/minuit2/src/VariableMetricBuilder.cxx�������������������������������0000644�0000000�0000000�00000032403�14332717401�022532� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/VariableMetricBuilder.h" #include "Minuit2/GradientCalculator.h" #include "Minuit2/MinimumState.h" #include "Minuit2/MinimumError.h" #include "Minuit2/FunctionGradient.h" #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnLineSearch.h" #include "Minuit2/MinimumSeed.h" #include "Minuit2/MnFcn.h" #include "Minuit2/MnMachinePrecision.h" #include "Minuit2/MnPosDef.h" #include "Minuit2/MnParabolaPoint.h" #include "Minuit2/LaSum.h" #include "Minuit2/LaProd.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnHesse.h" #include "Minuit2/MnPrint.h" #include #include namespace ROOT { namespace Minuit2 { double inner_product(const LAVector &, const LAVector &); void VariableMetricBuilder::AddResult(std::vector &result, const MinimumState &state) const { // // if (!store) store = StorageLevel(); // // store |= (result.size() == 0); // if (store) result.push_back(state); // else { // result.back() = state; // } if (TraceIter()) TraceIteration(result.size() - 1, result.back()); else { MnPrint print("VariableMetricBuilder", PrintLevel()); print.Info(MnPrint::Oneline(result.back(), result.size() - 1)); } } FunctionMinimum VariableMetricBuilder::Minimum(const MnFcn &fcn, const GradientCalculator &gc, const MinimumSeed &seed, const MnStrategy &strategy, unsigned int maxfcn, double edmval) const { MnPrint print("VariableMetricBuilder", PrintLevel()); // top level function to find minimum from a given initial seed // iterate on a minimum search in case of first attempt is not successful // to be consistent with F77 Minuit // in Minuit2 edm is correct and is ~ a factor of 2 smaller than F77Minuit // There are also a check for convergence if (edm < 0.1 edmval for exiting the loop) // LM: change factor to 2E-3 to be consistent with F77Minuit edmval *= 0.002; // set global printlevel to the local one so all calls to MN_INFO_MSG can be controlled in the same way // at exit of this function the BuilderPrintLevelConf object is destructed and automatically the // previous level will be restored // double edm = Estimator().Estimate(seed.Gradient(), seed.Error()); double edm = seed.State().Edm(); FunctionMinimum min(seed, fcn.Up()); if (seed.Parameters().Vec().size() == 0) { print.Warn("No free parameters."); return min; } if (!seed.IsValid()) { print.Error("Minimum seed invalid."); return min; } if (edm < 0.) { print.Error("Initial matrix not pos.def."); // assert(!seed.Error().IsPosDef()); return min; } std::vector result; if (StorageLevel() > 0) result.reserve(10); else result.reserve(2); // do actual iterations print.Info("Start iterating until Edm is <", edmval, "with call limit =", maxfcn); AddResult(result, seed.State()); // try first with a maxfxn = 80% of maxfcn int maxfcn_eff = maxfcn; int ipass = 0; bool iterate = false; do { iterate = false; print.Debug(ipass > 0 ? "Continue" : "Start", "iterating..."); min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval); // if max function call reached exits if (min.HasReachedCallLimit()) { print.Warn("FunctionMinimum is invalid, reached function call limit"); return min; } // second time check for validity of function Minimum if (ipass > 0) { if (!min.IsValid()) { print.Warn("FunctionMinimum is invalid after second try"); return min; } } // resulting edm of minimization edm = result.back().Edm(); // need to correct again for Dcovar: edm *= (1. + 3. * e.Dcovar()) ??? if ((strategy.Strategy() == 2) || (strategy.Strategy() == 1 && min.Error().Dcovar() > 0.05)) { print.Debug("MnMigrad will verify convergence and Error matrix; dcov =", min.Error().Dcovar()); MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(), maxfcn); print.Info("After Hessian"); AddResult(result, st); if (!st.IsValid()) { print.Warn("Invalid Hessian - exit the minimization"); break; } // check new edm edm = st.Edm(); print.Debug("New Edm", edm, "Requested", edmval); if (edm > edmval) { // be careful with machine precision and avoid too small edm double machineLimit = std::fabs(seed.Precision().Eps2() * result.back().Fval()); if (edm >= machineLimit) { iterate = true; print.Info("Tolerance not sufficient, continue minimization; " "Edm", edm, "Required", edmval); } else { print.Warn("Reached machine accuracy limit; Edm", edm, "is smaller than machine limit", machineLimit, "while", edmval, "was requested"); } } } // end loop on iterations // ? need a maximum here (or max of function calls is enough ? ) // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient) // no need to check that hesse calculation is done (if isnot done edm is OK anyway) // count the pass to exit second time when function Minimum is invalid // increase by 20% maxfcn for doing some more tests if (ipass == 0) maxfcn_eff = int(maxfcn * 1.3); ipass++; } while (iterate); // Add latest state (Hessian calculation) const MinimumState &latest = result.back(); // check edm (add a factor of 10 in tolerance ) if (edm > 10 * edmval) { min.Add(latest, FunctionMinimum::MnAboveMaxEdm); print.Warn("No convergence; Edm", edm, "is above tolerance", 10 * edmval); } else if (latest.Error().HasReachedCallLimit()) { // communicate to user that call limit was reached in MnHesse min.Add(latest, FunctionMinimum::MnReachedCallLimit); } else if (latest.Error().IsAvailable()) { // check if minimum had edm above max before if (min.IsAboveMaxEdm()) print.Info("Edm has been re-computed after Hesse; Edm", edm, "is now within tolerance"); min.Add(latest); } print.Debug("Minimum found", min); return min; } FunctionMinimum VariableMetricBuilder::Minimum(const MnFcn &fcn, const GradientCalculator &gc, const MinimumSeed &seed, std::vector &result, unsigned int maxfcn, double edmval) const { // function performing the minimum searches using the Variable Metric algorithm (MIGRAD) // perform first a line search in the - Vg direction and then update using the Davidon formula (Davidon Error // updator) stop when edm reached is less than required (edmval) // after the modification when I iterate on this functions, so it can be called many times, // the seed is used here only to get precision and construct the returned FunctionMinimum object MnPrint print("VariableMetricBuilder", PrintLevel()); const MnMachinePrecision &prec = seed.Precision(); // result.push_back(MinimumState(seed.Parameters(), seed.Error(), seed.Gradient(), edm, fcn.NumOfCalls())); const MinimumState &initialState = result.back(); double edm = initialState.Edm(); print.Debug("Initial State:", "\n Parameter:", initialState.Vec(), "\n Gradient:", initialState.Gradient().Vec(), "\n InvHessian:", initialState.Error().InvHessian(), "\n Edm:", initialState.Edm()); // iterate until edm is small enough or max # of iterations reached edm *= (1. + 3. * initialState.Error().Dcovar()); MnLineSearch lsearch; MnAlgebraicVector step(initialState.Gradient().Vec().size()); // keep also prevStep MnAlgebraicVector prevStep(initialState.Gradient().Vec().size()); MinimumState s0 = result.back(); do { // MinimumState s0 = result.back(); step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec(); print.Debug("Iteration", result.size(), "Fval", s0.Fval(), "numOfCall", fcn.NumOfCalls(), "\n Internal parameters", s0.Vec(), "\n Newton step", step); // check if derivatives are not zero if (inner_product(s0.Gradient().Vec(), s0.Gradient().Vec()) <= 0) { print.Debug("all derivatives are zero - return current status"); break; } double gdel = inner_product(step, s0.Gradient().Grad()); if (gdel > 0.) { print.Warn("Matrix not pos.def, gdel =", gdel, "> 0"); MnPosDef psdf; s0 = psdf(s0, prec); step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec(); // #ifdef DEBUG // std::cout << "After MnPosdef - Error " << s0.Error().InvHessian() << " Gradient " << // s0.Gradient().Vec() << " step " << step << std::endl; // #endif gdel = inner_product(step, s0.Gradient().Grad()); print.Warn("gdel =", gdel); if (gdel > 0.) { AddResult(result, s0); return FunctionMinimum(seed, result, fcn.Up()); } } MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec); // <= needed for case 0 <= 0 if (std::fabs(pp.Y() - s0.Fval()) <= std::fabs(s0.Fval()) * prec.Eps()) { print.Warn("No improvement in line search"); // no improvement exit (is it really needed LM ? in vers. 1.22 tried alternative ) // add new state when only fcn changes if (result.size() <= 1) AddResult(result, MinimumState(s0.Parameters(), s0.Error(), s0.Gradient(), s0.Edm(), fcn.NumOfCalls())); else // no need to re-store the state AddResult(result, MinimumState(pp.Y(), s0.Edm(), fcn.NumOfCalls())); break; } print.Debug("Result after line search :", "\n x =", pp.X(), "\n Old Fval =", s0.Fval(), "\n New Fval =", pp.Y(), "\n NFcalls =", fcn.NumOfCalls()); MinimumParameters p(s0.Vec() + pp.X() * step, pp.Y()); FunctionGradient g = gc(p, s0.Gradient()); edm = Estimator().Estimate(g, s0.Error()); if (std::isnan(edm)) { print.Warn("Edm is NaN; stop iterations"); AddResult(result, s0); return FunctionMinimum(seed, result, fcn.Up()); } if (edm < 0.) { print.Warn("Matrix not pos.def., try to make pos.def."); MnPosDef psdf; s0 = psdf(s0, prec); edm = Estimator().Estimate(g, s0.Error()); if (edm < 0.) { print.Warn("Matrix still not pos.def.; stop iterations"); AddResult(result, s0); return FunctionMinimum(seed, result, fcn.Up()); } } MinimumError e = ErrorUpdator().Update(s0, p, g); // avoid print Hessian that will invert the matrix print.Debug("Updated new point:", "\n Parameter:", p.Vec(), "\n Gradient:", g.Vec(), "\n InvHessian:", e.Matrix(), "\n Edm:", edm); // update the state s0 = MinimumState(p, e, g, edm, fcn.NumOfCalls()); if (StorageLevel() || result.size() <= 1) AddResult(result, s0); else // use a reduced state for not-final iterations AddResult(result, MinimumState(p.Fval(), edm, fcn.NumOfCalls())); // correct edm edm *= (1. + 3. * e.Dcovar()); print.Debug("Dcovar =", e.Dcovar(), "\tCorrected edm =", edm); } while (edm > edmval && fcn.NumOfCalls() < maxfcn); // end of iteration loop // save last result in case of no complete final states // when the result is filled above (reduced storage) the resulting state will not be valid // since they will not have parameter values and error // the line above will fill as last element a valid state if (!result.back().IsValid()) result.back() = s0; if (fcn.NumOfCalls() >= maxfcn) { print.Warn("Call limit exceeded"); return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnReachedCallLimit); } if (edm > edmval) { if (edm < std::fabs(prec.Eps2() * result.back().Fval())) { print.Warn("Machine accuracy limits further improvement"); return FunctionMinimum(seed, result, fcn.Up()); } else if (edm < 10 * edmval) { return FunctionMinimum(seed, result, fcn.Up()); } else { print.Warn("Iterations finish without convergence; Edm", edm, "Requested", edmval); return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnAboveMaxEdm); } } // std::cout<<"result.back().Error().Dcovar()= "< $ ) # We need to add the ROOT mathcore directories if build inside of ROOT without standalone) if(minuit2_inroot AND NOT minuit2_standalone) target_include_directories( Minuit2Math PUBLIC $ ) endif() target_compile_definitions( Minuit2Math PRIVATE MATH_NO_PLUGIN_MANAGER ) target_compile_definitions( Minuit2Math PUBLIC ROOT_Math_VecTypes MATHCORE_STANDALONE ) target_link_libraries(Minuit2Math PUBLIC Minuit2Common) target_compile_features(Minuit2Math PUBLIC cxx_auto_type cxx_static_assert) set_target_properties(Minuit2Math PROPERTIES CXX_EXTENSIONS OFF) install(TARGETS Minuit2Math EXPORT Minuit2Targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) install(FILES ${FIT_HEADERS} DESTINATION include/Minuit2/Fit) install(FILES ${MATH_HEADERS} DESTINATION include/Minuit2/Math) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mnbins.cxx����������������������������������������������0000644�0000000�0000000�00000005320�14332717401�017616� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include namespace ROOT { namespace Minuit2 { void mnbins(double a1, double a2, int naa, double &bl, double &bh, int &nb, double &bwid) { //*-*-*-*-*-*-*-*-*-*-*Compute reasonable histogram intervals*-*-*-*-*-*-*-*-* //*-* ====================================== //*-* Function TO DETERMINE REASONABLE HISTOGRAM INTERVALS //*-* GIVEN ABSOLUTE UPPER AND LOWER BOUNDS A1 AND A2 //*-* AND DESIRED MAXIMUM NUMBER OF BINS NAA //*-* PROGRAM MAKES REASONABLE BINNING FROM BL TO BH OF WIDTH BWID //*-* F. JAMES, AUGUST, 1974 , stolen for Minuit, 1988 //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* /* Local variables */ double awid, ah, al, sigfig, sigrnd, alb; int kwid, lwid, na = 0, log_; al = a1 < a2 ? a1 : a2; // al = std::min(a1,a2); // ah = std::max(a1,a2); ah = a1 > a2 ? a1 : a2; if (al == ah) ah = al + 1; //*-*- IF NAA .EQ. -1 , PROGRAM USES BWID INPUT FROM CALLING ROUTINE if (naa == -1) goto L150; L10: na = naa - 1; if (na < 1) na = 1; //*-*- GET NOMINAL BIN WIDTH IN EXPON FORM L20: awid = (ah - al) / double(na); log_ = int(log10(awid)); if (awid <= 1) --log_; sigfig = awid * pow(10.0, -log_); //*-*- ROUND MANTISSA UP TO 2, 2.5, 5, OR 10 if (sigfig > 2) goto L40; sigrnd = 2; goto L100; L40: if (sigfig > 2.5) goto L50; sigrnd = 2.5; goto L100; L50: if (sigfig > 5) goto L60; sigrnd = 5; goto L100; L60: sigrnd = 1; ++log_; L100: bwid = sigrnd * pow(10.0, log_); goto L200; //*-*- GET NEW BOUNDS FROM NEW WIDTH BWID L150: if (bwid <= 0) goto L10; L200: alb = al / bwid; lwid = int(alb); if (alb < 0) --lwid; bl = bwid * double(lwid); alb = ah / bwid + 1; kwid = int(alb); if (alb < 0) --kwid; bh = bwid * double(kwid); nb = kwid - lwid; if (naa > 5) goto L240; if (naa == -1) return; //*-*- REQUEST FOR ONE BIN IS DIFFICULT CASE if (naa > 1 || nb == 1) return; bwid *= 2; nb = 1; return; L240: if (nb << 1 != naa) return; ++na; goto L20; } /* mnbins_ */ } // namespace Minuit2 } // namespace ROOT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mndasum.cxx���������������������������������������������0000644�0000000�0000000�00000004542�14332717401�020001� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* dasum.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ #include namespace ROOT { namespace Minuit2 { double mndasum(unsigned int n, const double *dx, int incx) { /* System generated locals */ int i__1, i__2; double ret_val, d__1, d__2, d__3, d__4, d__5, d__6; /* Local variables */ int i__, m; double dtemp; int nincx, mp1; /* takes the sum of the absolute values. */ /* jack dongarra, linpack, 3/11/78. */ /* modified 3/93 to return if incx .le. 0. */ /* modified 12/3/93, array(1) declarations changed to array(*) */ /* Parameter adjustments */ --dx; /* Function Body */ ret_val = 0.; dtemp = 0.; if (n <= 0 || incx <= 0) { return ret_val; } if (incx == 1) { goto L20; } /* code for increment not equal to 1 */ nincx = n * incx; i__1 = nincx; i__2 = incx; for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) { dtemp += (d__1 = dx[i__], std::fabs(d__1)); /* L10: */ } ret_val = dtemp; return ret_val; /* code for increment equal to 1 */ /* clean-up loop */ L20: m = n % 6; if (m == 0) { goto L40; } i__2 = m; for (i__ = 1; i__ <= i__2; ++i__) { dtemp += (d__1 = dx[i__], std::fabs(d__1)); /* L30: */ } if (n < 6) { goto L60; } L40: mp1 = m + 1; i__2 = n; for (i__ = mp1; i__ <= i__2; i__ += 6) { dtemp = dtemp + (d__1 = dx[i__], std::fabs(d__1)) + (d__2 = dx[i__ + 1], std::fabs(d__2)) + (d__3 = dx[i__ + 2], std::fabs(d__3)) + (d__4 = dx[i__ + 3], std::fabs(d__4)) + (d__5 = dx[i__ + 4], std::fabs(d__5)) + (d__6 = dx[i__ + 5], std::fabs(d__6)); /* L50: */ } L60: ret_val = dtemp; return ret_val; } /* dasum_ */ } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mndaxpy.cxx���������������������������������������������0000644�0000000�0000000�00000004400�14332717401�020006� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* daxpy.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ namespace ROOT { namespace Minuit2 { int Mndaxpy(unsigned int n, double da, const double *dx, int incx, double *dy, int incy) { /* System generated locals */ int i__1; /* Local variables */ int i__, m, ix, iy, mp1; /* constant times a vector plus a vector. */ /* uses unrolled loops for increments equal to one. */ /* jack dongarra, linpack, 3/11/78. */ /* modified 12/3/93, array(1) declarations changed to array(*) */ /* Parameter adjustments */ --dy; --dx; /* Function Body */ if (n <= 0) { return 0; } if (da == 0.) { return 0; } if (incx == 1 && incy == 1) { goto L20; } /* code for unequal increments or equal increments */ /* not equal to 1 */ ix = 1; iy = 1; if (incx < 0) { ix = (-static_cast(n) + 1) * incx + 1; } if (incy < 0) { iy = (-static_cast(n) + 1) * incy + 1; } i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { dy[iy] += da * dx[ix]; ix += incx; iy += incy; /* L10: */ } return 0; /* code for both increments equal to 1 */ /* clean-up loop */ L20: m = n % 4; if (m == 0) { goto L40; } i__1 = m; for (i__ = 1; i__ <= i__1; ++i__) { dy[i__] += da * dx[i__]; /* L30: */ } if (n < 4) { return 0; } L40: mp1 = m + 1; i__1 = n; for (i__ = mp1; i__ <= i__1; i__ += 4) { dy[i__] += da * dx[i__]; dy[i__ + 1] += da * dx[i__ + 1]; dy[i__ + 2] += da * dx[i__ + 2]; dy[i__ + 3] += da * dx[i__ + 3]; /* L50: */ } return 0; } /* daxpy_ */ } // namespace Minuit2 } // namespace ROOT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mnddot.cxx����������������������������������������������0000644�0000000�0000000�00000004562�14332717401�017624� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* ddot.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ namespace ROOT { namespace Minuit2 { double mnddot(unsigned int n, const double *dx, int incx, const double *dy, int incy) { /* System generated locals */ int i__1; double ret_val; /* Local variables */ int i__, m; double dtemp; int ix, iy, mp1; /* forms the dot product of two vectors. */ /* uses unrolled loops for increments equal to one. */ /* jack dongarra, linpack, 3/11/78. */ /* modified 12/3/93, array(1) declarations changed to array(*) */ /* Parameter adjustments */ --dy; --dx; /* Function Body */ ret_val = 0.; dtemp = 0.; if (n <= 0) { return ret_val; } if (incx == 1 && incy == 1) { goto L20; } /* code for unequal increments or equal increments */ /* not equal to 1 */ ix = 1; iy = 1; if (incx < 0) { ix = (-static_cast(n) + 1) * incx + 1; } if (incy < 0) { iy = (-static_cast(n) + 1) * incy + 1; } i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { dtemp += dx[ix] * dy[iy]; ix += incx; iy += incy; /* L10: */ } ret_val = dtemp; return ret_val; /* code for both increments equal to 1 */ /* clean-up loop */ L20: m = n % 5; if (m == 0) { goto L40; } i__1 = m; for (i__ = 1; i__ <= i__1; ++i__) { dtemp += dx[i__] * dy[i__]; /* L30: */ } if (n < 5) { goto L60; } L40: mp1 = m + 1; i__1 = n; for (i__ = mp1; i__ <= i__1; i__ += 5) { dtemp = dtemp + dx[i__] * dy[i__] + dx[i__ + 1] * dy[i__ + 1] + dx[i__ + 2] * dy[i__ + 2] + dx[i__ + 3] * dy[i__ + 3] + dx[i__ + 4] * dy[i__ + 4]; /* L50: */ } L60: ret_val = dtemp; return ret_val; } /* ddot_ */ } // namespace Minuit2 } // namespace ROOT ����������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mndscal.cxx���������������������������������������������0000644�0000000�0000000�00000004110�14332717401�017745� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* dscal.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ namespace ROOT { namespace Minuit2 { int Mndscal(unsigned int n, double da, double *dx, int incx) { /* System generated locals */ int i__1, i__2; /* Local variables */ int i__, m, nincx, mp1; /* scales a vector by a constant. */ /* uses unrolled loops for increment equal to one. */ /* jack dongarra, linpack, 3/11/78. */ /* modified 3/93 to return if incx .le. 0. */ /* modified 12/3/93, array(1) declarations changed to array(*) */ /* Parameter adjustments */ --dx; /* Function Body */ if (n <= 0 || incx <= 0) { return 0; } if (incx == 1) { goto L20; } /* code for increment not equal to 1 */ nincx = n * incx; i__1 = nincx; i__2 = incx; for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) { dx[i__] = da * dx[i__]; /* L10: */ } return 0; /* code for increment equal to 1 */ /* clean-up loop */ L20: m = n % 5; if (m == 0) { goto L40; } i__2 = m; for (i__ = 1; i__ <= i__2; ++i__) { dx[i__] = da * dx[i__]; /* L30: */ } if (n < 5) { return 0; } L40: mp1 = m + 1; i__2 = n; for (i__ = mp1; i__ <= i__2; i__ += 5) { dx[i__] = da * dx[i__]; dx[i__ + 1] = da * dx[i__ + 1]; dx[i__ + 2] = da * dx[i__ + 2]; dx[i__ + 3] = da * dx[i__ + 3]; dx[i__ + 4] = da * dx[i__ + 4]; /* L50: */ } return 0; } /* dscal_ */ } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mndspmv.cxx���������������������������������������������0000644�0000000�0000000�00000022037�14332717401�020020� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* dspmv.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ namespace ROOT { namespace Minuit2 { bool mnlsame(const char *, const char *); int mnxerbla(const char *, int); int Mndspmv(const char *uplo, unsigned int n, double alpha, const double *ap, const double *x, int incx, double beta, double *y, int incy) { /* System generated locals */ int i__1, i__2; /* Local variables */ int info; double temp1, temp2; int i__, j, k; int kk, ix, iy, jx, jy, kx, ky; /* .. Scalar Arguments .. */ /* .. Array Arguments .. */ /* .. */ /* Purpose */ /* ======= */ /* DSPMV performs the matrix-vector operation */ /* y := alpha*A*x + beta*y, */ /* where alpha and beta are scalars, x and y are n element vectors and */ /* A is an n by n symmetric matrix, supplied in packed form. */ /* Parameters */ /* ========== */ /* UPLO - CHARACTER*1. */ /* On entry, UPLO specifies whether the Upper or Lower */ /* triangular part of the matrix A is supplied in the packed */ /* array AP as follows: */ /* UPLO = 'U' or 'u' The Upper triangular part of A is */ /* supplied in AP. */ /* UPLO = 'L' or 'l' The Lower triangular part of A is */ /* supplied in AP. */ /* Unchanged on exit. */ /* N - INTEGER. */ /* On entry, N specifies the order of the matrix A. */ /* N must be at least zero. */ /* Unchanged on exit. */ /* ALPHA - DOUBLE PRECISION. */ /* On entry, ALPHA specifies the scalar alpha. */ /* Unchanged on exit. */ /* AP - DOUBLE PRECISION array of DIMENSION at least */ /* ( ( n*( n + 1 ) )/2 ). */ /* Before entry with UPLO = 'U' or 'u', the array AP must */ /* contain the Upper triangular part of the symmetric matrix */ /* packed sequentially, column by column, so that AP( 1 ) */ /* contains a( 1, 1 ), AP( 2 ) and AP( 3 ) contain a( 1, 2 ) */ /* and a( 2, 2 ) respectively, and so on. */ /* Before entry with UPLO = 'L' or 'l', the array AP must */ /* contain the Lower triangular part of the symmetric matrix */ /* packed sequentially, column by column, so that AP( 1 ) */ /* contains a( 1, 1 ), AP( 2 ) and AP( 3 ) contain a( 2, 1 ) */ /* and a( 3, 1 ) respectively, and so on. */ /* Unchanged on exit. */ /* X - DOUBLE PRECISION array of dimension at least */ /* ( 1 + ( n - 1 )*abs( INCX ) ). */ /* Before entry, the incremented array X must contain the n */ /* element vector x. */ /* Unchanged on exit. */ /* INCX - INTEGER. */ /* On entry, INCX specifies the increment for the Elements of */ /* X. INCX must not be zero. */ /* Unchanged on exit. */ /* BETA - DOUBLE PRECISION. */ /* On entry, BETA specifies the scalar beta. When BETA is */ /* supplied as zero then Y need not be set on input. */ /* Unchanged on exit. */ /* Y - DOUBLE PRECISION array of dimension at least */ /* ( 1 + ( n - 1 )*abs( INCY ) ). */ /* Before entry, the incremented array Y must contain the n */ /* element vector y. On exit, Y is overwritten by the updated */ /* vector y. */ /* INCY - INTEGER. */ /* On entry, INCY specifies the increment for the Elements of */ /* Y. INCY must not be zero. */ /* Unchanged on exit. */ /* Level 2 Blas routine. */ /* -- Written on 22-October-1986. */ /* Jack Dongarra, Argonne National Lab. */ /* Jeremy Du Croz, Nag Central Office. */ /* Sven Hammarling, Nag Central Office. */ /* Richard Hanson, Sandia National Labs. */ /* .. Parameters .. */ /* .. Local Scalars .. */ /* .. External Functions .. */ /* .. External Subroutines .. */ /* .. */ /* .. Executable Statements .. */ /* Test the input parameters. */ /* Parameter adjustments */ --y; --x; --ap; /* Function Body */ info = 0; if (!mnlsame(uplo, "U") && !mnlsame(uplo, "L")) { info = 1; } // else if (n < 0) { // info = 2; // } else if (incx == 0) { info = 6; } else if (incy == 0) { info = 9; } if (info != 0) { mnxerbla("DSPMV ", info); return 0; } /* Quick return if possible. */ if ((n == 0) || (alpha == 0. && beta == 1.)) { return 0; } /* Set up the start points in X and Y. */ if (incx > 0) { kx = 1; } else { kx = 1 - (n - 1) * incx; } if (incy > 0) { ky = 1; } else { ky = 1 - (n - 1) * incy; } /* Start the operations. In this version the Elements of the array AP */ /* are accessed sequentially with one pass through AP. */ /* First form y := beta*y. */ if (beta != 1.) { if (incy == 1) { if (beta == 0.) { i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { y[i__] = 0.; /* L10: */ } } else { i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { y[i__] = beta * y[i__]; /* L20: */ } } } else { iy = ky; if (beta == 0.) { i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { y[iy] = 0.; iy += incy; /* L30: */ } } else { i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { y[iy] = beta * y[iy]; iy += incy; /* L40: */ } } } } if (alpha == 0.) { return 0; } kk = 1; if (mnlsame(uplo, "U")) { /* Form y when AP contains the Upper triangle. */ if (incx == 1 && incy == 1) { i__1 = n; for (j = 1; j <= i__1; ++j) { temp1 = alpha * x[j]; temp2 = 0.; k = kk; i__2 = j - 1; for (i__ = 1; i__ <= i__2; ++i__) { y[i__] += temp1 * ap[k]; temp2 += ap[k] * x[i__]; ++k; /* L50: */ } y[j] = y[j] + temp1 * ap[kk + j - 1] + alpha * temp2; kk += j; /* L60: */ } } else { jx = kx; jy = ky; i__1 = n; for (j = 1; j <= i__1; ++j) { temp1 = alpha * x[jx]; temp2 = 0.; ix = kx; iy = ky; i__2 = kk + j - 2; for (k = 0; k <= i__2 - kk; ++k) { y[iy] += temp1 * ap[k + kk]; temp2 += ap[k + kk] * x[ix]; ix += incx; iy += incy; /* L70: */ } y[jy] = y[jy] + temp1 * ap[kk + j - 1] + alpha * temp2; jx += incx; jy += incy; kk += j; /* L80: */ } } } else { /* Form y when AP contains the Lower triangle. */ if (incx == 1 && incy == 1) { i__1 = n; for (j = 1; j <= i__1; ++j) { temp1 = alpha * x[j]; temp2 = 0.; y[j] += temp1 * ap[kk]; k = kk + 1; i__2 = n; for (i__ = j + 1; i__ <= i__2; ++i__) { y[i__] += temp1 * ap[k]; temp2 += ap[k] * x[i__]; ++k; /* L90: */ } y[j] += alpha * temp2; kk += n - j + 1; /* L100: */ } } else { jx = kx; jy = ky; i__1 = n; for (j = 1; j <= i__1; ++j) { temp1 = alpha * x[jx]; temp2 = 0.; y[jy] += temp1 * ap[kk]; ix = jx; iy = jy; i__2 = kk + n - j; for (k = kk + 1; k <= i__2; ++k) { ix += incx; iy += incy; y[iy] += temp1 * ap[k]; temp2 += ap[k] * x[ix]; /* L110: */ } y[jy] += alpha * temp2; jx += incx; jy += incy; kk += n - j + 1; /* L120: */ } } } return 0; /* End of DSPMV . */ } /* dspmv_ */ } // namespace Minuit2 } // namespace ROOT �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mndspr.cxx����������������������������������������������0000644�0000000�0000000�00000015757�14332717401�017652� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* dspr.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ namespace ROOT { namespace Minuit2 { bool mnlsame(const char *, const char *); int mnxerbla(const char *, int); int mndspr(const char *uplo, unsigned int n, double alpha, const double *x, int incx, double *ap) { /* System generated locals */ int i__1, i__2; /* Local variables */ int info; double temp; int i__, j, k; int kk, ix, jx, kx = 0; /* .. Scalar Arguments .. */ /* .. Array Arguments .. */ /* .. */ /* Purpose */ /* ======= */ /* DSPR performs the symmetric rank 1 operation */ /* A := alpha*x*x' + A, */ /* where alpha is a real scalar, x is an n element vector and A is an */ /* n by n symmetric matrix, supplied in packed form. */ /* Parameters */ /* ========== */ /* UPLO - CHARACTER*1. */ /* On entry, UPLO specifies whether the Upper or Lower */ /* triangular part of the matrix A is supplied in the packed */ /* array AP as follows: */ /* UPLO = 'U' or 'u' The Upper triangular part of A is */ /* supplied in AP. */ /* UPLO = 'L' or 'l' The Lower triangular part of A is */ /* supplied in AP. */ /* Unchanged on exit. */ /* N - INTEGER. */ /* On entry, N specifies the order of the matrix A. */ /* N must be at least zero. */ /* Unchanged on exit. */ /* ALPHA - DOUBLE PRECISION. */ /* On entry, ALPHA specifies the scalar alpha. */ /* Unchanged on exit. */ /* X - DOUBLE PRECISION array of dimension at least */ /* ( 1 + ( n - 1 )*abs( INCX ) ). */ /* Before entry, the incremented array X must contain the n */ /* element vector x. */ /* Unchanged on exit. */ /* INCX - INTEGER. */ /* On entry, INCX specifies the increment for the Elements of */ /* X. INCX must not be zero. */ /* Unchanged on exit. */ /* AP - DOUBLE PRECISION array of DIMENSION at least */ /* ( ( n*( n + 1 ) )/2 ). */ /* Before entry with UPLO = 'U' or 'u', the array AP must */ /* contain the Upper triangular part of the symmetric matrix */ /* packed sequentially, column by column, so that AP( 1 ) */ /* contains a( 1, 1 ), AP( 2 ) and AP( 3 ) contain a( 1, 2 ) */ /* and a( 2, 2 ) respectively, and so on. On exit, the array */ /* AP is overwritten by the Upper triangular part of the */ /* updated matrix. */ /* Before entry with UPLO = 'L' or 'l', the array AP must */ /* contain the Lower triangular part of the symmetric matrix */ /* packed sequentially, column by column, so that AP( 1 ) */ /* contains a( 1, 1 ), AP( 2 ) and AP( 3 ) contain a( 2, 1 ) */ /* and a( 3, 1 ) respectively, and so on. On exit, the array */ /* AP is overwritten by the Lower triangular part of the */ /* updated matrix. */ /* Level 2 Blas routine. */ /* -- Written on 22-October-1986. */ /* Jack Dongarra, Argonne National Lab. */ /* Jeremy Du Croz, Nag Central Office. */ /* Sven Hammarling, Nag Central Office. */ /* Richard Hanson, Sandia National Labs. */ /* .. Parameters .. */ /* .. Local Scalars .. */ /* .. External Functions .. */ /* .. External Subroutines .. */ /* .. */ /* .. Executable Statements .. */ /* Test the input parameters. */ /* Parameter adjustments */ --ap; --x; /* Function Body */ info = 0; if (!mnlsame(uplo, "U") && !mnlsame(uplo, "L")) { info = 1; } // else if (n < 0) { // info = 2; // } else if (incx == 0) { info = 5; } if (info != 0) { mnxerbla("DSPR ", info); return 0; } /* Quick return if possible. */ if (n == 0 || alpha == 0.) { return 0; } /* Set the start point in X if the increment is not unity. */ if (incx <= 0) { kx = 1 - (n - 1) * incx; } else if (incx != 1) { kx = 1; } /* Start the operations. In this version the Elements of the array AP */ /* are accessed sequentially with one pass through AP. */ kk = 1; if (mnlsame(uplo, "U")) { /* Form A when Upper triangle is stored in AP. */ if (incx == 1) { i__1 = n; for (j = 1; j <= i__1; ++j) { if (x[j] != 0.) { temp = alpha * x[j]; k = kk; i__2 = j; for (i__ = 1; i__ <= i__2; ++i__) { ap[k] += x[i__] * temp; ++k; /* L10: */ } } kk += j; /* L20: */ } } else { jx = kx; i__1 = n; for (j = 1; j <= i__1; ++j) { if (x[jx] != 0.) { temp = alpha * x[jx]; ix = kx; i__2 = kk + j - 1; for (k = kk; k <= i__2; ++k) { ap[k] += x[ix] * temp; ix += incx; /* L30: */ } } jx += incx; kk += j; /* L40: */ } } } else { /* Form A when Lower triangle is stored in AP. */ if (incx == 1) { i__1 = n; for (j = 1; j <= i__1; ++j) { if (x[j] != 0.) { temp = alpha * x[j]; k = kk; i__2 = n; for (i__ = j; i__ <= i__2; ++i__) { ap[k] += x[i__] * temp; ++k; /* L50: */ } } kk = kk + n - j + 1; /* L60: */ } } else { jx = kx; i__1 = n; for (j = 1; j <= i__1; ++j) { if (x[jx] != 0.) { temp = alpha * x[jx]; ix = jx; i__2 = kk + n - j; for (k = kk; k <= i__2; ++k) { ap[k] += x[ix] * temp; ix += incx; /* L70: */ } } jx += incx; kk = kk + n - j + 1; /* L80: */ } } } return 0; /* End of DSPR . */ } /* dspr_ */ } // namespace Minuit2 } // namespace ROOT �����������������iminuit-2.24.0/extern/root/math/minuit2/src/mnlsame.cxx���������������������������������������������0000644�0000000�0000000�00000003515�14332717401�017770� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* lsame.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ #include namespace ROOT { namespace Minuit2 { bool mnlsame(const char *ca, const char *cb) { /* System generated locals */ bool ret_val = false; /* Local variables */ // integer inta, intb, zcode; /* -- LAPACK auxiliary routine (version 2.0) -- */ /* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., */ /* Courant Institute, Argonne National Lab, and Rice University */ /* January 31, 1994 */ /* .. Scalar Arguments .. */ /* .. */ /* Purpose */ /* ======= */ /* LSAME returns .TRUE. if CA is the same letter as CB regardless of */ /* case. */ /* Arguments */ /* ========= */ /* CA (input) CHARACTER*1 */ /* CB (input) CHARACTER*1 */ /* CA and CB specify the single characters to be compared. */ /* ===================================================================== */ /* .. Intrinsic Functions .. */ /* .. */ /* .. Local Scalars .. */ /* .. */ /* .. Executable Statements .. */ /* Test if the characters are equal */ int comp = std::strcmp(ca, cb); if (comp == 0) ret_val = true; return ret_val; } /* lsame_ */ } // namespace Minuit2 } // namespace ROOT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mnteigen.cxx��������������������������������������������0000644�0000000�0000000�00000015446�14332717401�020150� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* mneig.F -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ #include namespace ROOT { namespace Minuit2 { int mneigen(double *a, unsigned int ndima, unsigned int n, unsigned int mits, double *work, double precis) { // compute matrix eignevalues (transaltion from mneig.F of Minuit) /* System generated locals */ unsigned int a_dim1, a_offset, i__1, i__2, i__3; double r__1, r__2; /* Local variables */ double b, c__, f, h__; unsigned int i__, j, k, l, m = 0; double r__, s; unsigned int i0, i1, j1, m1, n1; double hh, gl, pr, pt; /* PRECIS is the machine precision EPSMAC */ /* Parameter adjustments */ a_dim1 = ndima; a_offset = 1 + a_dim1 * 1; a -= a_offset; --work; /* Function Body */ int ifault = 1; i__ = n; i__1 = n; for (i1 = 2; i1 <= i__1; ++i1) { l = i__ - 2; f = a[i__ + (i__ - 1) * a_dim1]; gl = (double)0.; if (l < 1) { goto L25; } i__2 = l; for (k = 1; k <= i__2; ++k) { /* Computing 2nd power */ r__1 = a[i__ + k * a_dim1]; gl += r__1 * r__1; } L25: /* Computing 2nd power */ r__1 = f; h__ = gl + r__1 * r__1; if (gl > (double)1e-35) { goto L30; } work[i__] = (double)0.; work[n + i__] = f; goto L65; L30: ++l; gl = std::sqrt(h__); if (f >= (double)0.) { gl = -gl; } work[n + i__] = gl; h__ -= f * gl; a[i__ + (i__ - 1) * a_dim1] = f - gl; f = (double)0.; i__2 = l; for (j = 1; j <= i__2; ++j) { a[j + i__ * a_dim1] = a[i__ + j * a_dim1] / h__; gl = (double)0.; i__3 = j; for (k = 1; k <= i__3; ++k) { gl += a[j + k * a_dim1] * a[i__ + k * a_dim1]; } if (j >= l) { goto L47; } j1 = j + 1; i__3 = l; for (k = j1; k <= i__3; ++k) { gl += a[k + j * a_dim1] * a[i__ + k * a_dim1]; } L47: work[n + j] = gl / h__; f += gl * a[j + i__ * a_dim1]; } hh = f / (h__ + h__); i__2 = l; for (j = 1; j <= i__2; ++j) { f = a[i__ + j * a_dim1]; gl = work[n + j] - hh * f; work[n + j] = gl; i__3 = j; for (k = 1; k <= i__3; ++k) { a[j + k * a_dim1] = a[j + k * a_dim1] - f * work[n + k] - gl * a[i__ + k * a_dim1]; } } work[i__] = h__; L65: --i__; } work[1] = (double)0.; work[n + 1] = (double)0.; i__1 = n; for (i__ = 1; i__ <= i__1; ++i__) { l = i__ - 1; if (work[i__] == (double)0. || l == 0) { goto L100; } i__3 = l; for (j = 1; j <= i__3; ++j) { gl = (double)0.; i__2 = l; for (k = 1; k <= i__2; ++k) { gl += a[i__ + k * a_dim1] * a[k + j * a_dim1]; } i__2 = l; for (k = 1; k <= i__2; ++k) { a[k + j * a_dim1] -= gl * a[k + i__ * a_dim1]; } } L100: work[i__] = a[i__ + i__ * a_dim1]; a[i__ + i__ * a_dim1] = (double)1.; if (l == 0) { goto L110; } i__2 = l; for (j = 1; j <= i__2; ++j) { a[i__ + j * a_dim1] = (double)0.; a[j + i__ * a_dim1] = (double)0.; } L110:; } n1 = n - 1; i__1 = n; for (i__ = 2; i__ <= i__1; ++i__) { i0 = n + i__ - 1; work[i0] = work[i0 + 1]; } work[n + n] = (double)0.; b = (double)0.; f = (double)0.; i__1 = n; for (l = 1; l <= i__1; ++l) { j = 0; h__ = precis * ((r__1 = work[l], std::fabs(r__1)) + (r__2 = work[n + l], std::fabs(r__2))); if (b < h__) { b = h__; } i__2 = n; for (m1 = l; m1 <= i__2; ++m1) { m = m1; if ((r__1 = work[n + m], std::fabs(r__1)) <= b) { goto L150; } } L150: if (m == l) { goto L205; } L160: if (j == mits) { return ifault; } ++j; pt = (work[l + 1] - work[l]) / (work[n + l] * (double)2.); r__ = std::sqrt(pt * pt + (double)1.); pr = pt + r__; if (pt < (double)0.) { pr = pt - r__; } h__ = work[l] - work[n + l] / pr; i__2 = n; for (i__ = l; i__ <= i__2; ++i__) { work[i__] -= h__; } f += h__; pt = work[m]; c__ = (double)1.; s = (double)0.; m1 = m - 1; i__ = m; i__2 = m1; for (i1 = l; i1 <= i__2; ++i1) { j = i__; --i__; gl = c__ * work[n + i__]; h__ = c__ * pt; if (std::fabs(pt) >= (r__1 = work[n + i__], std::fabs(r__1))) { goto L180; } c__ = pt / work[n + i__]; r__ = std::sqrt(c__ * c__ + (double)1.); work[n + j] = s * work[n + i__] * r__; s = (double)1. / r__; c__ /= r__; goto L190; L180: c__ = work[n + i__] / pt; r__ = std::sqrt(c__ * c__ + (double)1.); work[n + j] = s * pt * r__; s = c__ / r__; c__ = (double)1. / r__; L190: pt = c__ * work[i__] - s * gl; work[j] = h__ + s * (c__ * gl + s * work[i__]); i__3 = n; for (k = 1; k <= i__3; ++k) { h__ = a[k + j * a_dim1]; a[k + j * a_dim1] = s * a[k + i__ * a_dim1] + c__ * h__; a[k + i__ * a_dim1] = c__ * a[k + i__ * a_dim1] - s * h__; } } work[n + l] = s * pt; work[l] = c__ * pt; if ((r__1 = work[n + l], std::fabs(r__1)) > b) { goto L160; } L205: work[l] += f; } i__1 = n1; for (i__ = 1; i__ <= i__1; ++i__) { k = i__; pt = work[i__]; i1 = i__ + 1; i__3 = n; for (j = i1; j <= i__3; ++j) { if (work[j] >= pt) { goto L220; } k = j; pt = work[j]; L220:; } if (k == i__) { goto L240; } work[k] = work[i__]; work[i__] = pt; i__3 = n; for (j = 1; j <= i__3; ++j) { pt = a[j + i__ * a_dim1]; a[j + i__ * a_dim1] = a[j + k * a_dim1]; a[j + k * a_dim1] = pt; } L240:; } ifault = 0; return ifault; } /* mneig_ */ } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mntplot.cxx���������������������������������������������0000644�0000000�0000000�00000014371�14332717401�020033� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* mnplot.F -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ #include #include #include namespace ROOT { namespace Minuit2 { void mnbins(double, double, int, double &, double &, int &, double &); void mnplot(double *xpt, double *ypt, char *chpt, int nxypt, int npagwd, int npagln) { //*-*-*-*Plots points in array xypt onto one page with labelled axes*-*-*-*-* //*-* =========================================================== //*-* NXYPT is the number of points to be plotted //*-* XPT(I) = x-coord. of ith point //*-* YPT(I) = y-coord. of ith point //*-* CHPT(I) = character to be plotted at this position //*-* the input point arrays XPT, YPT, CHPT are destroyed. //*-* //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* // char cdot[] = "."; // char cslash[] = "/"; /* Local variables */ double xmin, ymin, xmax, ymax, savx, savy, yprt; double bwidx, bwidy, xbest, ybest, ax, ay, bx, by; double xvalus[12], any, dxx, dyy; int iten, i, j, k, maxnx, maxny, iquit, ni, linodd; int nxbest, nybest, km1, ibk, isp1, nx, ny, ks, ix; char ctemp[120]; bool overpr; char cline[120]; char chsav, chbest; /* Function Body */ //*-* Computing MIN maxnx = npagwd - 20 < 100 ? npagwd - 20 : 100; if (maxnx < 10) maxnx = 10; maxny = npagln; if (maxny < 10) maxny = 10; if (nxypt <= 1) return; xbest = xpt[0]; ybest = ypt[0]; chbest = chpt[0]; //*-*- order the points by decreasing y km1 = nxypt - 1; for (i = 1; i <= km1; ++i) { iquit = 0; ni = nxypt - i; for (j = 1; j <= ni; ++j) { if (ypt[j - 1] > ypt[j]) continue; savx = xpt[j - 1]; xpt[j - 1] = xpt[j]; xpt[j] = savx; savy = ypt[j - 1]; ypt[j - 1] = ypt[j]; ypt[j] = savy; chsav = chpt[j - 1]; chpt[j - 1] = chpt[j]; chpt[j] = chsav; iquit = 1; } if (iquit == 0) break; } //*-*- find extreme values xmax = xpt[0]; xmin = xmax; for (i = 1; i <= nxypt; ++i) { if (xpt[i - 1] > xmax) xmax = xpt[i - 1]; if (xpt[i - 1] < xmin) xmin = xpt[i - 1]; } dxx = (xmax - xmin) * .001; xmax += dxx; xmin -= dxx; mnbins(xmin, xmax, maxnx, xmin, xmax, nx, bwidx); ymax = ypt[0]; ymin = ypt[nxypt - 1]; if (ymax == ymin) ymax = ymin + 1; dyy = (ymax - ymin) * .001; ymax += dyy; ymin -= dyy; mnbins(ymin, ymax, maxny, ymin, ymax, ny, bwidy); any = (double)ny; //*-*- if first point is blank, it is an 'origin' if (chbest == ' ') goto L50; xbest = (xmax + xmin) * .5; ybest = (ymax + ymin) * .5; L50: //*-*- find Scale constants ax = 1 / bwidx; ay = 1 / bwidy; bx = -ax * xmin + 2; by = -ay * ymin - 2; //*-*- convert points to grid positions for (i = 1; i <= nxypt; ++i) { xpt[i - 1] = ax * xpt[i - 1] + bx; ypt[i - 1] = any - ay * ypt[i - 1] - by; } nxbest = int((ax * xbest + bx)); nybest = int((any - ay * ybest - by)); //*-*- print the points ny += 2; nx += 2; isp1 = 1; linodd = 1; overpr = false; for (i = 1; i <= ny; ++i) { for (ibk = 1; ibk <= nx; ++ibk) { cline[ibk - 1] = ' '; } cline[nx] = '\0'; cline[nx + 1] = '\0'; cline[0] = '.'; // not needed - but to avoid a wrongly reported compiler warning (see ROOT-6496) if (nx > 0) cline[nx - 1] = '.'; cline[nxbest - 1] = '.'; if (i != 1 && i != nybest && i != ny) goto L320; for (j = 1; j <= nx; ++j) { cline[j - 1] = '.'; } L320: yprt = ymax - double(i - 1) * bwidy; if (isp1 > nxypt) goto L350; //*-*- find the points to be plotted on this line for (k = isp1; k <= nxypt; ++k) { ks = int(ypt[k - 1]); if (ks > i) goto L345; ix = int(xpt[k - 1]); if (cline[ix - 1] == '.') goto L340; if (cline[ix - 1] == ' ') goto L340; if (cline[ix - 1] == chpt[k - 1]) continue; overpr = true; //*-*- OVERPR is true if one or more positions contains more than //*-*- one point cline[ix - 1] = '&'; continue; L340: cline[ix - 1] = chpt[k - 1]; } isp1 = nxypt + 1; goto L350; L345: isp1 = k; L350: if (linodd == 1 || i == ny) goto L380; linodd = 1; memcpy(ctemp, cline, 120); printf(" %s", (const char *)ctemp); goto L400; L380: // ctemp = cline; memcpy(ctemp, cline, 120); printf(" %14.7g ..%s", yprt, (const char *)ctemp); linodd = 0; L400: printf("\n"); } //*-*- print labels on x-axis every ten columns for (ibk = 1; ibk <= nx; ++ibk) { cline[ibk - 1] = ' '; if (ibk % 10 == 1) cline[ibk - 1] = '/'; } printf(" %s", cline); printf("\n"); for (ibk = 1; ibk <= 12; ++ibk) { xvalus[ibk - 1] = xmin + double(ibk - 1) * 10 * bwidx; } printf(" "); iten = (nx + 9) / 10; for (ibk = 1; ibk <= iten; ++ibk) { printf(" %9.4g", xvalus[ibk - 1]); } printf("\n"); if (overpr) { char chmess[] = " Overprint character is &"; printf(" ONE COLUMN=%13.7g%s", bwidx, (const char *)chmess); } else { char chmess[] = " "; printf(" ONE COLUMN=%13.7g%s", bwidx, (const char *)chmess); } printf("\n"); } /* mnplot_ */ } // namespace Minuit2 } // namespace ROOT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mnvert.cxx����������������������������������������������0000644�0000000�0000000�00000004014�14332717401�017642� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ #include "Minuit2/MnMatrix.h" #include namespace ROOT { namespace Minuit2 { /** Inverts a symmetric matrix. Matrix is first scaled to have all ones on the diagonal (equivalent to change of units) but no pivoting is done since matrix is positive-definite. */ int mnvert(MnAlgebraicSymMatrix &a) { unsigned int nrow = a.Nrow(); MnAlgebraicVector s(nrow); MnAlgebraicVector q(nrow); MnAlgebraicVector pp(nrow); for (unsigned int i = 0; i < nrow; i++) { double si = a(i, i); if (si < 0.) return 1; s(i) = 1. / std::sqrt(si); } for (unsigned int i = 0; i < nrow; i++) for (unsigned int j = i; j < nrow; j++) a(i, j) *= (s(i) * s(j)); for (unsigned i = 0; i < nrow; i++) { unsigned int k = i; if (a(k, k) == 0.) return 1; q(k) = 1. / a(k, k); pp(k) = 1.; a(k, k) = 0.; unsigned int kp1 = k + 1; if (k != 0) { for (unsigned int j = 0; j < k; j++) { pp(j) = a(j, k); q(j) = a(j, k) * q(k); a(j, k) = 0.; } } if (k != nrow - 1) { for (unsigned int j = kp1; j < nrow; j++) { pp(j) = a(k, j); q(j) = -a(k, j) * q(k); a(k, j) = 0.; } } for (unsigned int j = 0; j < nrow; j++) for (k = j; k < nrow; k++) a(j, k) += (pp(j) * q(k)); } for (unsigned int j = 0; j < nrow; j++) for (unsigned int k = j; k < nrow; k++) a(j, k) *= (s(j) * s(k)); return 0; } } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/extern/root/math/minuit2/src/mnxerbla.cxx��������������������������������������������0000644�0000000�0000000�00000004370�14332717401�020144� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// @(#)root/minuit2:$Id$ // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005 /********************************************************************** * * * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT * * * **********************************************************************/ /* xerbla.f -- translated by f2c (version 20010320). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ #include "Minuit2/MnConfig.h" #include "Minuit2/MnPrint.h" namespace ROOT { namespace Minuit2 { /* Table of constant values */ // static integer c__1 = 1; int mnxerbla(const char *srname, int info) { /* Format strings */ // static char fmt_9999[] = "(\002 ** On entry to \002,a6,\002 Parameter nu\// mber \002,i2,\002 had \002,\002an // illegal Value\002)"; /* -- LAPACK auxiliary routine (version 3.0) -- */ /* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., */ /* Courant Institute, Argonne National Lab, and Rice University */ /* September 30, 1994 */ /* .. Scalar Arguments .. */ /* .. */ /* Purpose */ /* ======= */ /* XERBLA is an Error handler for the LAPACK routines. */ /* It is called by an LAPACK routine if an input Parameter has an */ /* invalid Value. A message is printed and execution stops. */ /* Installers may consider modifying the STOP statement in order to */ /* call system-specific exception-handling facilities. */ /* Arguments */ /* ========= */ /* SRNAME (input) CHARACTER*6 */ /* The Name of the routine which called XERBLA. */ /* INFO (input) INTEGER */ /* The position of the invalid Parameter in the Parameter list */ /* of the calling routine. */ /* ===================================================================== */ /* .. Executable Statements .. */ MnPrint print("mnxerbla"); print.Warn("On entry to", srname, "Parameter number", info, "had an illegal Value"); /* End of XERBLA */ return 0; } /* xerbla_ */ } // namespace Minuit2 } // namespace ROOT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/pyproject.toml�����������������������������������������������������������������������0000644�0000000�0000000�00000007131�14332717401�013123� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[build-system] requires = ["scikit-build-core[pyproject]>=0.3", "pybind11"] build-backend = "scikit_build_core.build" [project] name = "iminuit" description = "Jupyter-friendly Python frontend for MINUIT2 in C++" version = "2.24.0" maintainers = [ { name = "Hans Dembinski" }, { email = "hans.dembinski@gmail.com" }, ] readme = "README.rst" requires-python = ">=3.8" license = { file = "LICENSE" } classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Science/Research", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Programming Language :: C++", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Mathematics", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: Unix", "Operating System :: MacOS", ] dependencies = ["numpy >= 1.21", "typing_extensions;python_version < '3.9'"] [project.urls] repository = "http://github.com/scikit-hep/iminuit" documentation = "https://iminuit.readthedocs.io" [project.optional-dependencies] test = [ "coverage", "cython", # ipywidgets 8.0.5 and 8.0.6 are broken # see https://github.com/jupyter-widgets/ipywidgets/issues/3731 "ipywidgets<8.0.5", "ipykernel", # needed by ipywidgets 8.0.6 "joblib", "jacobi", "matplotlib", "numpy", "numba", "numba-stats", "pytest", "scipy", "tabulate", "boost_histogram", "resample", "unicodeitplus", "pydantic", "annotated_types", ] doc = [ "sphinx<7", "sphinx-rtd-theme", # needs sphinx<7 "nbsphinx", "nbconvert", "nbformat", "jupyter_client", "ipykernel", "jax", "jaxlib", ] [tool.scikit-build] minimum-version = "0.3" build-dir = "build/{wheel_tag}" sdist.exclude = ["extern/root"] sdist.include = ["extern/root/math/minuit2/inc", "extern/root/math/minuit2/src"] [tool.pytest.ini_options] minversion = "6.0" addopts = "-q -ra --ff" testpaths = ["tests"] filterwarnings = [ "error::numpy.VisibleDeprecationWarning", "error::DeprecationWarning", ] [tool.ruff] select = ["E", "F", "D"] extend-ignore = ["D203", "D212"] [tool.ruff.pydocstyle] convention = "numpy" [tool.ruff.per-file-ignores] "test_*.py" = ["B", "D"] ".ci/*.py" = ["D"] "bench/*.py" = ["D"] "doc/*.py" = ["D"] "setup.py" = ["D"] "cmake_ext.py" = ["D"] [tool.mypy] ignore_missing_imports = true allow_redefinition = true plugins = "numpy.typing.mypy_plugin" pretty = true files = ["src"] no_implicit_optional = false [tool.cibuildwheel] # update skip when numpy wheels become available skip = ["*-musllinux_*", "cp31?-win32", "cp31?-manylinux_i686"] test-requires = "pytest" test-command = "python -m pytest {package}/tests" test-skip = ["*universal2:arm64"] # to match numpy, we use manylinux2014 for cp310+ manylinux-x86_64-image = "manylinux2014" manylinux-i686-image = "manylinux2014" [tool.cibuildwheel.environment] # this makes sure that we build only on platforms that have a corresponding numpy wheel PIP_ONLY_BINARY = ":all:" [[tool.cibuildwheel.overrides]] # to match numpy, we use manylinux2010 for cp36 to cp39 select = "cp3?-*" manylinux-x86_64-image = "manylinux2010" manylinux-i686-image = "manylinux2010" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/application.cpp������������������������������������������������������������������0000644�0000000�0000000�00000000747�14332717401�014013� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void bind_application(py::module m) { py::class_(m, "MnApplication") .def("__call__", &MnApplication::operator()) .def_property("precision", &MnApplication::Precision, &MnApplication::SetPrecision) .def_property_readonly("strategy", &MnApplication::Strategy) ; } �������������������������iminuit-2.24.0/src/contours.cpp���������������������������������������������������������������������0000644�0000000�0000000�00000001246�14332717401�013357� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void bind_contours(py::module m) { py::class_(m, "MnContours") .def(py::init()) .def("__call__", [](const MnContours& self, unsigned ix, unsigned iy, unsigned npoints) { const auto ce = self.Contour(ix, iy, npoints); return py::make_tuple(ce.XMinosError(), ce.YMinosError(), ce()); }) ; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/equal.cpp������������������������������������������������������������������������0000644�0000000�0000000�00000000510�14332717401�012603� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "equal.hpp" #include #include #include bool nan_equal(double a, double b) { return std::isnan(a) == std::isnan(b) || a == b; } namespace std { bool operator==(const vector& a, const vector& b) { return equal(a.begin(), a.end(), b.begin(), b.end()); } } // namespace std ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/equal.hpp������������������������������������������������������������������������0000644�0000000�0000000�00000003050�14332717401�012612� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef IMINUIT_EQUAL_HPP #define IMINUIT_EQUAL_HPP #include bool nan_equal(double a, double b); namespace std { bool operator==(const vector& a, const vector& b); } // namespace std namespace ROOT { namespace Minuit2 { class MnStrategy; bool operator==(const MnStrategy& a, const MnStrategy& b); class MinuitParameter; bool operator==(const MinuitParameter& a, const MinuitParameter& b); class MnUserCovariance; bool operator==(const MnUserCovariance& a, const MnUserCovariance& b); class MnUserParameterState; bool operator==(const MnUserParameterState& a, const MnUserParameterState& b); class MnMachinePrecision; bool operator==(const MnMachinePrecision& a, const MnMachinePrecision& b); class MnUserTransformation; bool operator==(const MnUserTransformation& a, const MnUserTransformation& b); class LAVector; bool operator==(const LAVector& a, const LAVector& b); class LASymMatrix; bool operator==(const LASymMatrix& a, const LASymMatrix& b); class MinimumError; bool operator==(const MinimumError& a, const MinimumError& b); class FunctionGradient; bool operator==(const FunctionGradient& a, const FunctionGradient& b); class MinimumParameters; bool operator==(const MinimumParameters& a, const MinimumParameters& b); class MinimumState; bool operator==(const MinimumState& a, const MinimumState& b); class MinimumSeed; bool operator==(const MinimumSeed& a, const MinimumSeed& b); class FunctionMinimum; bool operator==(const FunctionMinimum& a, const FunctionMinimum& b); } // namespace Minuit2 } // namespace ROOT #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/fcn.cpp��������������������������������������������������������������������������0000644�0000000�0000000�00000007667�14332717401�012266� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "fcn.hpp" #include "type_caster.hpp" #include #include #include #include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; FCN::FCN(py::object fcn, py::object grad, bool array_call, double errordef) : fcn_{fcn}, grad_{grad}, array_call_{array_call}, errordef_{errordef} { auto util = py::module_::import("iminuit.util"); auto address_of_cfunc = util.attr("_address_of_cfunc"); auto address = py::cast(address_of_cfunc(fcn_)); if (address) { MnPrint("FCN").Debug("using cfunc"); cfcn_ = reinterpret_cast(address); array_call_ = true; } } double FCN::operator()(const std::vector& x) const { ++nfcn_; if (array_call_) { if (cfcn_) { return cfcn_(x.size(), x.data()); } else { py::array_t a(static_cast(x.size()), x.data()); return check_value(py::cast(fcn_(a)), x); } } return check_value(py::cast(fcn_(*py::cast(x))), x); } std::vector FCN::Gradient(const std::vector& x) const { ++ngrad_; if (array_call_) { py::array_t a(static_cast(x.size()), x.data()); return check_vector(py::cast>(grad_(a)), x); } return check_vector(py::cast>(grad_(*py::cast(x))), x); } double FCN::Up() const { return errordef_; } void set_errordef(FCN& self, double value) { if (value > 0) { self.SetUp(value); } else throw std::invalid_argument("errordef must be a positive number"); } std::string error_message(const std::vector& x) { std::ostringstream msg; msg << "result is NaN for [ "; for (auto&& xi : x) msg << xi << " "; msg << "]"; return msg.str(); } double FCN::check_value(double r, const std::vector& x) const { if (std::isnan(r)) { if (throw_nan_) throw std::runtime_error(error_message(x)); else { MnPrint("FCN").Warn([&](std::ostream& os) { os << error_message(x); }); } } return r; } std::vector FCN::check_vector(std::vector r, const std::vector& x) const { bool has_nan = false; for (auto&& ri : r) has_nan |= std::isnan(ri); if (has_nan) { if (throw_nan_) throw std::runtime_error(error_message(x)); else { MnPrint("FCN::Gradient").Warn([&](std::ostream& os) { os << error_message(x); }); } } return r; } double FCN::ndata() const { if (py::hasattr(fcn_, "ndata")) return py::cast(fcn_.attr("ndata")); return std::numeric_limits::quiet_NaN(); } void bind_fcn(py::module m) { py::class_(m, "FCNBase"); py::class_(m, "FCN") .def(py::init()) .def("gradient", &FCN::Gradient) .def("_ndata", &FCN::ndata) .def_readwrite("_nfcn", &FCN::nfcn_) .def_readwrite("_ngrad", &FCN::ngrad_) .def_readwrite("_throw_nan", &FCN::throw_nan_) .def_property("_errordef", &FCN::Up, &set_errordef) .def_readonly("_array_call", &FCN::array_call_) .def_readonly("_fcn", &FCN::fcn_) .def_readonly("_grad", &FCN::grad_) .def("__call__", &FCN::operator()) .def(py::pickle( [](const FCN& self) { return py::make_tuple(self.fcn_, self.grad_, self.array_call_, self.errordef_, self.throw_nan_, self.nfcn_, self.ngrad_); }, [](py::tuple tp) { if (tp.size() != 7) throw std::runtime_error("invalid state"); FCN fcn{tp[0], tp[1], tp[2].cast(), tp[3].cast()}; fcn.throw_nan_ = tp[4].cast(); fcn.nfcn_ = tp[5].cast(); fcn.ngrad_ = tp[6].cast(); return fcn; })) ; } �������������������������������������������������������������������������iminuit-2.24.0/src/fcn.hpp��������������������������������������������������������������������������0000644�0000000�0000000�00000002552�14332717401�012257� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include namespace py = pybind11; /** Function interface for Minuit2. Calls the underlying Python function which computes the objective function. The interface of this class is defined by the abstract base class FCNGradientBase, which itself derives from FCNBase. It calls the function with the parameter vector converted into positional arguments or by passing a single Numpy array, depending on the value of array_call_. */ struct FCN : ROOT::Minuit2::FCNGradientBase { FCN(py::object fcn, py::object grad, bool array_call, double errordef); double operator()(const std::vector& x) const override; std::vector Gradient(const std::vector&) const override; bool CheckGradient() const override { return false; } double Up() const override; void SetUp(double x) { errordef_ = x; } double check_value(double r, const std::vector& x) const; std::vector check_vector(std::vector r, const std::vector& x) const; double ndata() const; py::object fcn_, grad_; bool array_call_; mutable double errordef_; using cfcn_t = double (*)(std::uint32_t, const double*); cfcn_t cfcn_ = nullptr; bool throw_nan_ = false; mutable unsigned nfcn_ = 0, ngrad_ = 0; }; ������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/functionminimum.cpp��������������������������������������������������������������0000644�0000000�0000000�00000005443�14332717401�014727� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "equal.hpp" #include "fcn.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; FunctionMinimum init(const FCN& fcn, const MnUserParameterState& st, const MnStrategy& str, double edm_goal); FunctionMinimum init2(const MnUserTransformation& tr, py::sequence par, py::sequence cov, py::sequence grad, double fval, double up, double edm_goal, int nfcn, int max_nfcn, bool exact_hess_inv); py::tuple fmin_getstate(const FunctionMinimum&); FunctionMinimum fmin_setstate(py::tuple); bool hesse_failed_workaround(const FunctionMinimum& self) { // Calling FunctionMinimum::HesseFailed is not reliable. // It calls MinimumError::HesseFailed, but this is false if // there were the failures MinimumError::MnReachedCallLimit // or MinimumError::MnInvertFailed, which cannot be queried // from FunctionMinimum. // // As a workaround, we return true if covariance exists, but // is not accurate or made positive definite. return self.HasCovariance() && !(self.HasPosDefCovar() || self.HasMadePosDefCovar()); } void bind_functionminimum(py::module m) { py::class_(m, "FunctionMinimum") .def(py::init(&init)) .def(py::init(&init2)) .def_property_readonly("state", &FunctionMinimum::UserState) .def_property_readonly("edm", &FunctionMinimum::Edm) .def_property_readonly("fval", &FunctionMinimum::Fval) .def_property_readonly("is_valid", &FunctionMinimum::IsValid) // No need to wrap HasValidParameters // .def_property_readonly("has_valid_parameters", // &FunctionMinimum::HasValidParameters) .def_property_readonly("has_accurate_covar", &FunctionMinimum::HasAccurateCovar) .def_property_readonly("has_posdef_covar", &FunctionMinimum::HasPosDefCovar) .def_property_readonly("has_made_posdef_covar", &FunctionMinimum::HasMadePosDefCovar) .def_property_readonly("hesse_failed", hesse_failed_workaround) .def_property_readonly("has_covariance", &FunctionMinimum::HasCovariance) .def_property_readonly("is_above_max_edm", &FunctionMinimum::IsAboveMaxEdm) .def_property_readonly("has_reached_call_limit", &FunctionMinimum::HasReachedCallLimit) .def_property("errordef", &FunctionMinimum::Up, &FunctionMinimum::SetErrorDef) .def(py::self == py::self) .def(py::pickle(&fmin_getstate, &fmin_setstate)) ; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/functionminimum_extra.cpp��������������������������������������������������������0000644�0000000�0000000�00000013542�14332717401�016131� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "equal.hpp" #include "fcn.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; MinimumSeed make_seed(const FCN& fcn, const MnUserFcn& mfcn, const MnUserParameterState& st, const MnStrategy& str) { MnSeedGenerator gen; if (fcn.grad_.is_none()) { Numerical2PGradientCalculator gc(mfcn, st.Trafo(), str); return gen(mfcn, gc, st, str); } AnalyticalGradientCalculator gc(fcn, st.Trafo()); return gen(mfcn, gc, st, str); } FunctionMinimum init(const FCN& fcn, const MnUserParameterState& st, const MnStrategy& str, double edm_goal) { MnUserFcn mfcn(fcn, st.Trafo()); MinimumSeed seed = make_seed(fcn, mfcn, st, str); const auto& val = seed.Parameters().Vec(); const auto n = seed.Trafo().VariableParameters(); MnAlgebraicVector err(n); for (unsigned int i = 0; i < n; i++) { err(i) = std::sqrt(2. * mfcn.Up() * seed.Error().InvHessian()(i, i)); } MinimumParameters minp(val, err, seed.Fval()); std::vector minstv(1, MinimumState(minp, seed.Edm(), fcn.nfcn_)); if (minstv.back().Edm() < edm_goal) return FunctionMinimum(seed, minstv, fcn.Up()); return FunctionMinimum(seed, minstv, fcn.Up(), FunctionMinimum::MnAboveMaxEdm); } FunctionMinimum init2(const MnUserTransformation& trafo, py::sequence py_par, py::sequence py_hess_inv, py::sequence py_grad, double fval, double up, double edm_goal, int nfcn, int max_nfcn, bool exact_hess_inv) { // if parameters are fixed, py_par, py_cov, and py_grad only contain free parameters const auto n = trafo.VariableParameters(); MnAlgebraicVector val{n}, step{n}, g{n}, g2{n}; MnAlgebraicSymMatrix hess_inv{n}; for (unsigned i = 0; i < n; ++i) { const auto iext = trafo.ExtOfInt(i); val(i) = trafo.Ext2int(iext, py_par[i].cast()); const auto di = trafo.DInt2Ext(i, val(i)); for (unsigned k = 0; k <= i; ++k) { const auto py_hess_invi = py_hess_inv[i].cast(); const auto dk = trafo.DInt2Ext(k, val(k)); hess_inv(i, k) = py_hess_invi[k].cast() / di / dk; } step(i) = std::sqrt(hess_inv(i, i)); g(i) = py_grad[i].cast() / di; // TODO: use diagonal elements of inverted IntCovariance for G2 g2(i) = 1.0 / step(i); } MinimumParameters minp{val, step, fval}; MinimumError err{hess_inv, exact_hess_inv ? 0. : 1.}; FunctionGradient grad{g, g2, step}; const double edm = VariableMetricEDMEstimator().Estimate(grad, err); MinimumState st{minp, err, grad, edm, nfcn}; MinimumSeed seed{st, trafo}; FunctionMinimum fm(seed, {}, up); if (nfcn > max_nfcn) fm.Add(st, FunctionMinimum::MnReachedCallLimit); else if (edm < edm_goal) fm.Add(st, FunctionMinimum::MnValid); else fm.Add(st, FunctionMinimum::MnAboveMaxEdm); return fm; } py::tuple seed2py(const MinimumSeed& seed) { return py::make_tuple(seed.State(), seed.Trafo(), seed.IsValid()); } MinimumSeed py2seed(py::tuple tp) { static_assert(std::is_standard_layout(), ""); MinimumSeed seed(tp[0].cast(), tp[1].cast()); struct Layout { MinimumState fState; MnUserTransformation fTrafo; bool fValid; }; auto& ptr = reinterpret_cast&>(seed); auto d = ptr.get(); d->fValid = tp[2].cast(); return seed; } namespace ROOT { namespace Minuit2 { bool operator==(const MnUserTransformation& a, const MnUserTransformation& b) { return a.Precision() == b.Precision() && a.Parameters() == b.Parameters(); } bool operator==(const LAVector& a, const LAVector& b) { return std::equal(a.Data(), a.Data() + a.size(), b.Data(), b.Data() + b.size()); } bool operator==(const LASymMatrix& a, const LASymMatrix& b) { return std::equal(a.Data(), a.Data() + a.size(), b.Data(), b.Data() + b.size()); } bool operator==(const MinimumError& a, const MinimumError& b) { return a.InvHessian() == b.InvHessian() && a.Dcovar() == b.Dcovar() && a.IsValid() == b.IsValid() && a.IsPosDef() == b.IsPosDef() && a.IsMadePosDef() == b.IsMadePosDef() && a.HesseFailed() == b.HesseFailed() && a.InvertFailed() == b.InvertFailed() && a.IsAvailable() == b.IsAvailable(); } bool operator==(const FunctionGradient& a, const FunctionGradient& b) { return a.Grad() == b.Grad() && a.G2() == b.G2() && a.Gstep() == b.Gstep() && a.IsValid() == b.IsValid() && a.IsAnalytical() == b.IsAnalytical(); } bool operator==(const MinimumParameters& a, const MinimumParameters& b) { return a.Vec() == b.Vec() && a.Dirin() == b.Dirin() && a.Fval() == b.Fval() && a.IsValid() == b.IsValid() && a.HasStepSize() == b.HasStepSize(); } bool operator==(const MinimumState& a, const MinimumState& b) { return a.Parameters() == b.Parameters() && a.Error() == b.Error() && a.Gradient() == b.Gradient() && a.Fval() == b.Fval() && a.Edm() == b.Edm() && a.NFcn() == b.NFcn(); } bool operator==(const MinimumSeed& a, const MinimumSeed& b) { return a.State() == b.State() && a.Trafo() == b.Trafo() && a.IsValid() == b.IsValid(); } bool operator==(const FunctionMinimum& a, const FunctionMinimum& b) { return a.Seed() == b.Seed() && a.Up() == b.Up() && a.States() == b.States() && a.IsAboveMaxEdm() == b.IsAboveMaxEdm() && a.HasReachedCallLimit() == b.HasReachedCallLimit() && a.UserState() == b.UserState(); } } // namespace Minuit2 } // namespace ROOT ��������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/functionminimum_pickle.cpp�������������������������������������������������������0000644�0000000�0000000�00000002706�14332717401�016255� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; py::tuple seed2py(const MinimumSeed& seed); MinimumSeed py2seed(py::tuple tp); py::tuple fmin_getstate(const FunctionMinimum& self) { return py::make_tuple(seed2py(self.Seed()), self.Up(), self.States(), self.IsAboveMaxEdm(), self.HasReachedCallLimit(), self.UserState()); } FunctionMinimum fmin_setstate(py::tuple tp) { static_assert(std::is_standard_layout(), ""); if (tp.size() != 6) throw std::runtime_error("invalid state"); struct Layout { MinimumSeed fSeed; std::vector fStates; double fErrorDef; bool fAboveMaxEdm; bool fReachedCallLimit; mutable MnUserParameterState fUserState; }; auto seed = py2seed(tp[0]); auto up = tp[1].cast(); FunctionMinimum fm(seed, up); // evil workaround, will segfault or cause UB if source layout changes auto& ptr = reinterpret_cast&>(fm); auto d = ptr.get(); d->fStates = tp[2].cast>(); d->fAboveMaxEdm = tp[3].cast(); d->fReachedCallLimit = tp[4].cast(); d->fUserState = tp[5].cast(); return fm; } ����������������������������������������������������������iminuit-2.24.0/src/hesse.cpp������������������������������������������������������������������������0000644�0000000�0000000�00000002270�14332717401�012610� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void update_fmin(MnHesse& self, const FCNBase& fcn, FunctionMinimum& min, unsigned maxcalls, float maxedm) { // We reset call counter here in contrast to MnHesse.cxx:83 MnUserFcn mfcn(fcn, min.UserState().Trafo(), 0); // Run Hesse MinimumState st = self(mfcn, min.State(), min.UserState().Trafo(), maxcalls); // Need to re-evalute status of minimum, EDM could now be over max EDM or // maxcalls could be exhausted, see MnMigrad.cxx:187 const auto edm = st.Edm(); if (edm > 10 * maxedm) min.Add(st, FunctionMinimum::MnAboveMaxEdm); else if (st.Error().HasReachedCallLimit()) // communicate to user that call limit was reached in MnHesse min.Add(st, FunctionMinimum::MnReachedCallLimit); else if (st.Error().IsAvailable()) min.Add(st); } void bind_hesse(py::module m) { py::class_(m, "MnHesse") .def(py::init()) .def("__call__", update_fmin) ; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/iminuit/__init__.py��������������������������������������������������������������0000644�0000000�0000000�00000001315�14332717401�014563� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Jupyter-friendly Python interface for the Minuit2 library in C++. Basic usage example:: from iminuit import Minuit def fcn(x, y, z): return (x - 2) ** 2 + (y - 3) ** 2 + (z - 4) ** 2 m = Minuit(fcn, x=0, y=0, z=0) m.migrad() m.hesse() print(m.values) # 'x': 2, 'y': 3, 'z': 4 print(m.errors) # 'x': 1, 'y': 1, 'z': 1 Further information: * Code: https://github.com/scikit-hep/iminuit * Docs: https://iminuit.readthedocs.io """ from iminuit.minuit import Minuit from iminuit.minimize import minimize from iminuit.util import describe from importlib import metadata __version__ = metadata.version("iminuit") __all__ = ["Minuit", "minimize", "describe", "__version__"] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/iminuit/_deprecated.py�����������������������������������������������������������0000644�0000000�0000000�00000002427�14332717401�015270� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������import warnings from numpy import VisibleDeprecationWarning class deprecated: def __init__(self, reason): self._reason = reason def __call__(self, func): def decorated_func(*args, **kwargs): warnings.warn( f"{func.__name__} is deprecated: {self._reason}", category=VisibleDeprecationWarning, stacklevel=2, ) return func(*args, **kwargs) decorated_func.__name__ = func.__name__ decorated_func.__doc__ = "deprecated: " + self._reason return decorated_func class deprecated_parameter: def __init__(self, **replacements): self._replacements = replacements def __call__(self, func): def decorated_func(*args, **kwargs): for new, old in self._replacements.items(): if old in kwargs: warnings.warn( f"keyword {old!r} is deprecated, please use {new!r}", category=VisibleDeprecationWarning, stacklevel=2, ) kwargs[new] = kwargs[old] del kwargs[old] return func(*args, **kwargs) decorated_func.__name__ = func.__name__ return decorated_func �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/iminuit/_hide_modules.py���������������������������������������������������������0000644�0000000�0000000�00000001740�14332717401�015626� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import contextlib from importlib.abc import MetaPathFinder class HiddenModules(MetaPathFinder): def __init__(self, modules): super().__init__() self.modules = modules def find_spec(self, fullname, path, target=None): if fullname in self.modules: raise ModuleNotFoundError( f"{fullname!r} is hidden", name=fullname, path=path ) @contextlib.contextmanager def hide_modules(*modules, reload=None): saved = {} for m in tuple(sys.modules): for to_hide in modules: if m.startswith(to_hide): saved[m] = sys.modules[m] del sys.modules[m] sys.meta_path.insert(0, HiddenModules(modules)) if reload and reload in sys.modules: del sys.modules[reload] yield if reload and reload in sys.modules: del sys.modules[reload] sys.meta_path = sys.meta_path[1:] for name, mod in saved.items(): sys.modules[name] = mod ��������������������������������iminuit-2.24.0/src/iminuit/_optional_dependencies.py������������������������������������������������0000644�0000000�0000000�00000001140�14332717401�017512� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������import contextlib import warnings from iminuit.warnings import OptionalDependencyWarning @contextlib.contextmanager def optional_module_for(functionality, *, replace=None, stacklevel=3): try: yield except ModuleNotFoundError as e: package = e.name.split(".")[0] if replace: package = replace.get(package, package) msg = ( f"{functionality} requires optional package {package!r}. " f"Install {package!r} manually to enable this functionality." ) warnings.warn(msg, OptionalDependencyWarning, stacklevel=stacklevel) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������iminuit-2.24.0/src/iminuit/_repr_html.py������������������������������������������������������������0000644�0000000�0000000�00000020125�14332717401�015157� 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������from ._repr_text import pdg_format, matrix_format, fmin_fields, _parse_latex good_style = "background-color:#92CCA6;color:black" bad_style = "background-color:#c15ef7;color:black" warn_style = "background-color:#FFF79A;color:black" class ColorGradient: """Color gradient.""" _steps = None def __init__(self, *steps): self._steps = steps def __call__(self, v): st = self._steps z = 0.0 if v < st[0][0]: z = 0.0 i = 0 elif v >= st[-1][0]: z = 1.0 i = -2 else: i = 0 for i in range(len(st) - 1): if st[i][0] <= v < st[i + 1][0]: break z = (v - st[i][0]) / (st[i + 1][0] - st[i][0]) az = 1.0 - z a = st[i] b = st[i + 1] return (az * a[1] + z * b[1], az * a[2] + z * b[2], az * a[3] + z * b[3]) def rgb(self, v): return "rgb(%.0f,%.0f,%.0f)" % self(v) def to_str(tag): lines = [] def visit(x, level): indent = " " * level if len(x) == 1: lines.append(indent + x[0]) else: b, *ch, e = x lines.append(indent + b) for x in ch: visit(x, level + 1) lines.append(indent + e) visit(tag, 0) return "\n".join(lines) def tag(name, *args, **kwargs): # sort keys so that order is same on all platforms head = "<" + name for k in sorted(kwargs): v = kwargs[k] head += f' {k}="{v}"' head += ">" tail = "" % name if len(args) == 0: return [head + tail] if len(args) == 1 and isinstance(args[0], str): return ["{} {} {}".format(head, args[0], tail)] return [head, *args, tail] def table(*args, **kwargs): return tag("table", *args, **kwargs) def tr(*args, **kwargs): return tag("tr", *args, **kwargs) def th(*args, **kwargs): return tag("th", *args, **kwargs) def td(*args, **kwargs): return tag("td", *args, **kwargs) def good(x, should_be, alt_style=bad_style): return good_style if x == should_be else alt_style def fmin(fm): ff = fmin_fields(fm) if fm.hesse_failed: cov_style = bad_style elif fm.has_accurate_covar: cov_style = good_style else: cov_style = warn_style return to_str( table( tr( th( ff[0], colspan=2, style="text-align:center", title="Minimizer", ), ), tr( td( ff[1], style="text-align:left", title="Minimum value of function", ), td( ff[2], style="text-align:center", title=( "Total number of function and (optional) gradient evaluations" ), ), ), tr( td( ff[3], style="text-align:left", title="Estimated distance to minimum and goal", ), td( ff[4], style="text-align:center", title="Total run time of algorithms", ), ), tr( td( ff[5], style="text-align:center;" + good(fm.is_valid, True), ), td( ff[6], style="text-align:center;" + good(fm.is_above_max_edm, False), ), ), tr( td( ff[7], style="text-align:center;" + good(fm.has_parameters_at_limit, False, warn_style), ), td( ff[8], style="text-align:center;" + good(fm.has_reached_call_limit, False), ), ), tr( td(ff[9], style="text-align:center;" + cov_style), td(ff[10], style="text-align:center;" + cov_style), ), ) ) def params(mps): # body rows = [] for i, mp in enumerate(mps): me = mp.merror if me: v, e, mem, mep = pdg_format(mp.value, mp.error, *me) else: v, e = pdg_format(mp.value, mp.error) mem = "" mep = "" rows.append( tr( th(str(i)), td(_parse_latex(mp.name)), td(v), td(e), td(mem), td(mep), td("%.3G" % mp.lower_limit if mp.lower_limit is not None else ""), td("%.3G" % mp.upper_limit if mp.upper_limit is not None else ""), td("yes" if mp.is_fixed else ("CONST" if mp.is_const else "")), ) ) return to_str( table( # header tr( td(), th("Name", title="Variable name"), th("Value", title="Value of parameter"), th("Hesse Error", title="Hesse error"), th("Minos Error-", title="Minos lower error"), th("Minos Error+", title="Minos upper error"), th("Limit-", title="Lower limit of the parameter"), th("Limit+", title="Upper limit of the parameter"), th("Fixed", title="Is the parameter fixed in the fit"), ), # body *rows, ) ) def merrors(mes): mes = mes.values() header = [td()] error = [th("Error", title="Lower and upper minos error of the parameter")] valid = [th("Valid", title="Validity of lower/upper minos error")] limit = [th("At Limit", title="Did scan hit limit of any parameter?")] maxfcn = [th("Max FCN", title="Did scan hit function call limit?")] newmin = [th("New Min", title="New minimum found when doing scan?")] for me in mes: header.append( th( _parse_latex(me.name), colspan=2, title="Parameter name", style="text-align:center", ) ) error += [td(x) for x in pdg_format(None, me.lower, me.upper)] valid += [ td(str(x), style=good(x, True)) for x in (me.lower_valid, me.upper_valid) ] limit += [ td(str(x), style=good(x, False)) for x in (me.at_lower_limit, me.at_upper_limit) ] maxfcn += [ td(str(x), style=good(x, False)) for x in (me.at_lower_max_fcn, me.at_upper_max_fcn) ] newmin += [ td(str(x), style=good(x, False)) for x in (me.lower_new_min, me.upper_new_min) ] return to_str( table( tr(*header), tr(*error), tr(*valid), tr(*limit), tr(*maxfcn), tr(*newmin), ) ) def matrix(arr): names = [_parse_latex(x) for x in arr._var2pos] n = len(names) nums = matrix_format(arr) grad = ColorGradient( (-1.0, 120.0, 120.0, 250.0), (0.0, 250.0, 250.0, 250.0), (1.0, 250.0, 100.0, 100.0), ) rows = [] for i, v in enumerate(names): cols = [th(v)] di = arr[i, i] ** 0.5 for j in range(len(names)): val = arr[i, j] dj = arr[j, j] ** 0.5 if i == j: t = td(nums[n * i + j]) else: corr = val / (di * dj + 1e-100) color = grad.rgb(corr) t = td( nums[n * i + j] + ( f" ({corr:.3f})" if abs(corr - val) > 1e-3 else "" ), style="background-color:" + color + ";color:black", ) cols.append(t) rows.append(tr(*cols)) return to_str(table(tr(td(), *[th(v) for v in names]), *rows)) iminuit-2.24.0/src/iminuit/_repr_text.py0000644000000000000000000001634614332717401015211 0ustar00from .pdg_format import _round, _strip from iminuit._optional_dependencies import optional_module_for import re import numpy as np def pdg_format(value, *errors): if value is None: strings, nexp = _round((0, *errors), None, None) strings = strings[1:] else: strings, nexp = _round((value, *errors), None, None) strings = _strip(strings) if nexp != 0: for i, s in enumerate(strings): if s[-1] in "fn": continue m = None if i == 0: m = re.match(r"(-?)0\.0+$", s) if m: s = m.group(1) + "0" suffix = "" if not m: suffix = "e%i" % nexp s += suffix strings[i] = s return strings def format_row(widths, *args) -> str: return ( "".join( ("│{0:^%i}" % w if w > 0 else "│ {0:%i}" % (-w - 1)).format(a) for (w, a) in zip(widths, args) ) + "│" ) def format_line(widths, edges): s = edges[0] for w, e in zip(widths, edges[1:]): s += "─" * abs(w) s += e return s def fmin_fields(fm): rc = fm.reduced_chi2 s_ncall = f"Nfcn = {fm.nfcn}" if fm.ngrad > 0: s_ncall += f", Ngrad = {fm.ngrad}" if fm.hesse_failed: covariance_msg1 = "Hesse FAILED" if fm.has_reached_call_limit: covariance_msg2 = "ABOVE call limit" else: assert not fm.has_posdef_covar covariance_msg2 = "Covariance NOT pos. def." else: if fm.has_covariance: covariance_msg1 = "Hesse ok" if fm.has_accurate_covar: covariance_msg2 = "Covariance accurate" elif fm.has_made_posdef_covar: covariance_msg2 = "Covariance FORCED pos. def." else: covariance_msg2 = "Covariance APPROXIMATE" else: covariance_msg1 = "Hesse not run" covariance_msg2 = "NO covariance" return [ f"{fm.algorithm}", f"FCN = {fm.fval:.4g}" + (f" (χ²/ndof = {rc:.1f})" if not np.isnan(rc) else ""), s_ncall, f"EDM = {fm.edm:.3g} (Goal: {fm.edm_goal:.3g})", f"time = {fm.time:.1f} sec" if fm.time >= 0.1 else "", f"{'Valid' if fm.is_valid else 'INVALID'} Minimum", f"{'ABOVE' if fm.is_above_max_edm else 'Below'} EDM threshold (goal x 10)", f"{'SOME' if fm.has_parameters_at_limit else 'No'} parameters at limit", f"{'ABOVE' if fm.has_reached_call_limit else 'Below'} call limit", covariance_msg1, covariance_msg2, ] def fmin(fm): ff = fmin_fields(fm) w = (73,) l1 = format_line(w, "┌┐") i1 = format_row(w, ff[0] + " ") w = (-34, 38) l2 = format_line(w, "├┬┤") i2 = format_row(w, *ff[1:3]) i3 = format_row(w, *ff[3:5]) w = (34, 38) l3 = format_line(w, "├┼┤") v1 = format_row(w, *ff[5:7]) l4 = format_line(w, "├┼┤") v2 = format_row(w, *ff[7:9]) l5 = format_line(w, "├┼┤") v3 = format_row(w, *ff[9:]) l6 = format_line(w, "└┴┘") return "\n".join((l1, i1, l2, i2, i3, l3, v1, l4, v2, l5, v3, l6)) def params(mps): vnames = [_parse_latex(mp.name) for mp in mps] name_width = max([4] + [len(x) for x in vnames]) num_width = max(2, len(f"{len(mps) - 1}")) ws = (-num_width - 1, -name_width - 2, 11, 11, 12, 12, 9, 9, 7) h = format_row( ws, "", "Name", "Value", "Hesse Err", "Minos Err-", "Minos Err+", "Limit-", "Limit+", "Fixed", ) ni = len(ws) - 1 l1 = format_line(ws, "┌" + "┬" * ni + "┐") l2 = format_line(ws, "├" + "┼" * ni + "┤") lines = [l1, h, l2] for i, mp in enumerate(mps): me = mp.merror if me: val, err, mel, meu = pdg_format(mp.value, mp.error, *me) else: val, err = pdg_format(mp.value, mp.error) mel = "" meu = "" lines.append( format_row( ws, str(i), vnames[i], val, err, mel, meu, "%g" % mp.lower_limit if mp.lower_limit is not None else "", "%g" % mp.upper_limit if mp.upper_limit is not None else "", "yes" if mp.is_fixed else "CONST" if mp.is_const else "", ) ) ln3 = format_line(ws, "└" + "┴" * ni + "┘") lines.append(ln3) return "\n".join(lines) def merrors(mes): mes = mes.values() n = len(mes) ws = [10] + [23] * n l1 = format_line(ws, "┌" + "┬" * n + "┐") header = format_row(ws, "", *(_parse_latex(m.name) for m in mes)) ws = [10] + [11] * (2 * n) l2 = format_line(ws, "├" + "┼┬" * n + "┤") l3 = format_line(ws, "└" + "┴" * n * 2 + "┘") x = [] for m in mes: mel, meu = pdg_format(None, m.lower, m.upper) x.append(mel) x.append(meu) error = format_row(ws, "Error", *x) x = [] for m in mes: x.append(str(m.lower_valid)) x.append(str(m.upper_valid)) valid = format_row(ws, "Valid", *x) x = [] for m in mes: x.append(str(m.at_lower_limit)) x.append(str(m.at_upper_limit)) at_limit = format_row(ws, "At Limit", *x) x = [] for m in mes: x.append(str(m.at_lower_max_fcn)) x.append(str(m.at_upper_max_fcn)) max_fcn = format_row(ws, "Max FCN", *x) x = [] for m in mes: x.append(str(m.lower_new_min)) x.append(str(m.upper_new_min)) new_min = format_row(ws, "New Min", *x) return "\n".join((l1, header, l2, error, valid, at_limit, max_fcn, new_min, l3)) def matrix(arr): names = [_parse_latex(x) for x in arr._var2pos] n = len(arr) nums = matrix_format(arr) def row_fmt(args): s = "│ " + args[0] + " │" for x in args[1:]: s += " " + x s += " │" return s first_row_width = max(len(v) for v in names) row_width = max(first_row_width, max(len(v) for v in nums)) v_names = [("{:>%is}" % first_row_width).format(x) for x in names] h_names = [("{:>%is}" % row_width).format(x) for x in names] val_fmt = ("{:>%is}" % row_width).format w = (first_row_width + 2, (row_width + 1) * len(names) + 1) l1 = format_line(w, "┌┬┐") l2 = format_line(w, "├┼┤") l3 = format_line(w, "└┴┘") header = row_fmt([" " * first_row_width] + h_names) lines = [l1, header, l2] for i, vn in enumerate(v_names): lines.append(row_fmt([vn] + [val_fmt(nums[n * i + j]) for j in range(n)])) lines.append(l3) return "\n".join(lines) def matrix_format(matrix): r = [] for i in range(matrix.shape[0]): for j in range(matrix.shape[1]): if i == j: r.append(f"{matrix[i, i]:.3g}") else: x = pdg_format(matrix[i, j], matrix[i, i], matrix[j, j])[0] r.append(x) return r def _parse_latex(s): if s.startswith("$") and s.endswith("$"): with optional_module_for("rendering simple LaTeX"): import unicodeitplus return unicodeitplus.parse(s) return s iminuit-2.24.0/src/iminuit/cost.py0000644000000000000000000020653214332717401014004 0ustar00""" Standard cost functions to minimize for statistical fits. We provide these for convenience, so that you do not have to write your own for standard fits. The cost functions optionally use Numba to accelerate some calculations, if Numba is installed. What to use when ---------------- - Fit a normalised probability density to data - Data are not binned: :class:`UnbinnedNLL` - Data are binned: :class:`BinnedNLL`, also supports histogram of weighted samples - Fit a density to data, density is not normalised - Data are not binned: :class:`ExtendedUnbinnedNLL` - Data are binned: :class:`ExtendedBinnedNLL`, also supports histogram of weighted samples - Fit a template to binned data with bin-wise uncertainties on the template: :class:`Template`, which also supports weighted data and weighted templates - Fit of a function f(x) to (x, y, yerror) pairs with normal-distributed fluctuations. x is one- or multi-dimensional, y is one-dimensional. - y values contain no outliers: :class:`LeastSquares` - y values contain outliers: :class:`LeastSquares` with loss function set to "soft_l1" - Include constraints from external fits or apply regularisation: :class:`NormalConstraint` Combining cost functions ------------------------ All cost functions can be added, which generates a new combined cost function. Parameters with the same name are shared between component cost functions. Use this to constrain one or several parameters with different data sets and using different statistical models for each data set. Gaussian penalty terms can also be added to the cost function to introduce external knowledge about a parameter. Model parameter limits ---------------------- The Minuit algorithms support box constrains in parameter space. A user-defined model can declare that a parameter is only valid over an interval on the real line with the ``Annotated`` type annotation, see :class:`iminuit.Minuit` for details. A typical example is the sigma parameter of a normal distribution, which must be positive. The cost functions defined here propagate this information to :class:`iminuit.Minuit`. Note: The :class:`Template` declares that the template amplitudes must be non-negative, which is usually the right choice, however, it may be desirable to fit templates which can have negative amplitudes. To achieve this, simply reset the limits with :attr:`iminuit.Minuit.limits` after creating the Minuit instance. Notes ----- The cost functions defined here have been optimized with knowledge about implementation details of Minuit to give the highest accucary and the most robust results, so they should perform well. If you have trouble with your own implementations, try these. The binned versions of the log-likelihood fits support weighted samples. For each bin of the histogram, the sum of weights and the sum of squared weights is needed then, see class documentation for details. """ from .util import ( describe, merge_signatures, PerformanceWarning, _smart_sampling, _detect_log_spacing, ) from .typing import Model, LossFunction import numpy as np from numpy.typing import ArrayLike, NDArray from collections.abc import Sequence as ABCSequence import abc from typing import ( List, Tuple, Union, Sequence, Collection, Dict, Any, Iterable, Optional, overload, ) import warnings from ._deprecated import deprecated_parameter __all__ = [ "CHISQUARE", "NEGATIVE_LOG_LIKELIHOOD", "BohmZechTransform", "chi2", "multinominal_chi2", "poisson_chi2", "template_chi2_jsc", "template_chi2_da", "template_nll_asy", "Cost", "CostSum", "Constant", "BinnedNLL", "UnbinnedNLL", "ExtendedBinnedNLL", "ExtendedUnbinnedNLL", "Template", "LeastSquares", ] CHISQUARE = 1.0 NEGATIVE_LOG_LIKELIHOOD = 0.5 _TINY_FLOAT = np.finfo(float).tiny def _safe_log(x): # guard against x = 0 return np.log(np.maximum(_TINY_FLOAT, x)) def _unbinned_nll(x): return -np.sum(_safe_log(x)) def _z_squared(y, ye, ym): z = (y - ym) / ye return z * z def _soft_l1_loss(z_sqr): return np.sum(2 * (np.sqrt(1 + z_sqr) - 1)) def _soft_l1_cost(y, ye, ym): return _soft_l1_loss(_z_squared(y, ye, ym)) def _replace_none(x, replacement): if x is None: return replacement return x class BohmZechTransform: """ Apply Bohm-Zech transform. See Bohm and Zech, NIMA 748 (2014) 1-6. :meta private: """ _scale: np.ndarray _obs: np.ndarray def __init__(self, val: ArrayLike, var: ArrayLike): """ Initialize transformer with data value and variance. Parameters ---------- val : array-like Observed values. var : array-like Estimated variance of observed values. """ val, var = np.atleast_1d(val, var) self._scale = np.ones_like(val) np.divide(val, var, out=self._scale, where=var > 0) self._obs = val * self._scale @overload def __call__(self, val: ArrayLike) -> Tuple[NDArray, NDArray]: ... # pragma: no cover @overload def __call__( self, val: ArrayLike, var: ArrayLike ) -> Tuple[NDArray, NDArray, NDArray]: ... # pragma: no cover def __call__(self, val, var=None): """ Return precomputed scaled data and scaled prediction. Parameters ---------- val : array-like Predicted values. var : array-like, optional Predicted variance. Returns ------- (obs, pred) or (obs, pred, pred_var) """ val = np.atleast_1d(val) s = self._scale if var is None: return self._obs, val * s var = np.atleast_1d(var) return self._obs, val * s, var * s**2 def chi2(y: ArrayLike, ye: ArrayLike, ym: ArrayLike) -> float: """ Compute (potentially) chi2-distributed cost. The value returned by this function is chi2-distributed, if the observed values are normally distributed around the expected values with the provided standard deviations. Parameters ---------- y : array-like Observed values. ye : array-like Uncertainties of values. ym : array-like Expected values. Returns ------- float Const function value. """ y, ye, ym = np.atleast_1d(y, ye, ym) return np.sum(_z_squared(y, ye, ym)) def multinominal_chi2(n: ArrayLike, mu: ArrayLike) -> float: """ Compute asymptotically chi2-distributed cost for binomially-distributed data. See Baker & Cousins, NIM 221 (1984) 437-442. Parameters ---------- n : array-like Observed counts. mu : array-like Expected counts per bin. Must satisfy sum(mu) == sum(n). Returns ------- float Cost function value. Notes ----- The implementation makes the result asymptotically chi2-distributed and keeps the sum small near the minimum, which helps to maximise the numerical accuracy for Minuit. """ n, mu = np.atleast_1d(n, mu) return 2 * np.sum(n * (_safe_log(n) - _safe_log(mu))) def poisson_chi2(n: ArrayLike, mu: ArrayLike) -> float: """ Compute asymptotically chi2-distributed cost for Poisson-distributed data. See Baker & Cousins, NIM 221 (1984) 437-442. Parameters ---------- n : array-like Observed counts. mu : array-like Expected counts. Returns ------- float Cost function value. Notes ----- The implementation makes the result asymptotically chi2-distributed, which helps to maximise the numerical accuracy for Minuit. """ n, mu = np.atleast_1d(n, mu) return 2 * np.sum(mu - n + n * (_safe_log(n) - _safe_log(mu))) def template_chi2_jsc(n: ArrayLike, mu: ArrayLike, mu_var: ArrayLike) -> float: """ Compute asymptotically chi2-distributed cost for a template fit. J.S. Conway, PHYSTAT 2011, https://doi.org/10.48550/arXiv.1103.0354 Parameters ---------- n : array-like Observed counts. mu : array-like Expected counts. This is the sum of the normalised templates scaled with the component yields. Must be positive everywhere. mu_var : array-like Expected variance of mu. Must be positive everywhere. Returns ------- float Asymptotically chi-square-distributed test statistic. Notes ----- The implementation deviates slightly from the paper by making the result asymptotically chi2-distributed, which helps to maximise the numerical accuracy for Minuit. """ n, mu, mu_var = np.atleast_1d(n, mu, mu_var) beta_var = mu_var / mu**2 # Eq. 15 from https://doi.org/10.48550/arXiv.2206.12346 p = 0.5 - 0.5 * mu * beta_var beta = p + np.sqrt(p**2 + n * beta_var) return poisson_chi2(n, mu * beta) + np.sum((beta - 1) ** 2 / beta_var) def template_chi2_da(n: ArrayLike, mu: ArrayLike, mu_var: ArrayLike) -> float: """ Compute asymptotically chi2-distributed cost for a template fit. H.P. Dembinski, A. Abdelmotteleb, https://doi.org/10.48550/arXiv.2206.12346 Parameters ---------- n : array-like Observed counts. mu : array-like Expected counts. This is the sum of the normalised templates scaled with the component yields. mu_var : array-like Expected variance of mu. Must be positive everywhere. Returns ------- float Asymptotically chi-square-distributed test statistic. """ n, mu, mu_var = np.atleast_1d(n, mu, mu_var) k = mu**2 / mu_var beta = (n + k) / (mu + k) return poisson_chi2(n, mu * beta) + poisson_chi2(k, k * beta) def template_nll_asy(n: ArrayLike, mu: ArrayLike, mu_var: ArrayLike) -> float: """ Compute marginalized negative log-likelikihood for a template fit. This is the negative logarithm of equation 3.15 of the paper by C.A. Argüelles, A. Schneider, T. Yuan, https://doi.org/10.1007/JHEP06(2019)030. The authors use a Bayesian approach and integrate over the nuisance parameters. Like the other Barlow-Beeston-lite methods, this is an approximation. The resulting likelihood cannot be turned into an asymptotically chi-square distributed test statistic as detailed in Baker & Cousins, NIM 221 (1984) 437-442. Parameters ---------- n : array-like Observed counts. mu : array-like Expected counts. This is the sum of the normalised templates scaled with the component yields. mu_var : array-like Expected variance of mu. Must be positive everywhere. Returns ------- float Negative log-likelihood function value. """ from scipy.special import loggamma as lg n, mu, mu_var = np.atleast_1d(n, mu, mu_var) alpha = mu**2 / mu_var + 1 beta = mu / mu_var return -np.sum( alpha * np.log(beta) + lg(n + alpha) - (lg(n + 1) + (n + alpha) * np.log(1 + beta) + lg(alpha)) ) # If numba is available, use it to accelerate computations in float32 and float64 # precision. Fall back to plain numpy for float128 which is not currently supported # by numba. try: from numba import njit as jit from numba.extending import overload as nb_overload @nb_overload(_safe_log, inline="always") def _ol_safe_log(x): return _safe_log # pragma: no cover @nb_overload(_z_squared, inline="always") def _ol_z_squared(y, ye, ym): return _z_squared # pragma: no cover _unbinned_nll_np = _unbinned_nll _unbinned_nll_nb = jit( nogil=True, cache=True, error_model="numpy", )(_unbinned_nll_np) def _unbinned_nll(x): if x.dtype in (np.float32, np.float64): return _unbinned_nll_nb(x) # fallback to numpy for float128 return _unbinned_nll_np(x) _multinominal_chi2_np = multinominal_chi2 _multinominal_chi2_nb = jit( nogil=True, cache=True, error_model="numpy", )(_multinominal_chi2_np) def multinominal_chi2(n: ArrayLike, mu: ArrayLike) -> float: # noqa n, mu = np.atleast_1d(n, mu) if mu.dtype in (np.float32, np.float64): return _multinominal_chi2_nb(n, mu) # fallback to numpy for float128 return _multinominal_chi2_np(n, mu) multinominal_chi2.__doc__ = _multinominal_chi2_np.__doc__ _poisson_chi2_np = poisson_chi2 _poisson_chi2_nb = jit( nogil=True, cache=True, error_model="numpy", )(_poisson_chi2_np) def poisson_chi2(n: ArrayLike, mu: ArrayLike) -> float: # noqa n, mu = np.atleast_1d(n, mu) if mu.dtype in (np.float32, np.float64): return _poisson_chi2_nb(n, mu) # fallback to numpy for float128 return _poisson_chi2_np(n, mu) poisson_chi2.__doc__ = _poisson_chi2_np.__doc__ _chi2_np = chi2 _chi2_nb = jit( nogil=True, cache=True, error_model="numpy", )(_chi2_np) def chi2(y: ArrayLike, ye: ArrayLike, ym: ArrayLike) -> float: # noqa y, ye, ym = np.atleast_1d(y, ye, ym) if ym.dtype in (np.float32, np.float64): return _chi2_nb(y, ye, ym) # fallback to numpy for float128 return _chi2_np(y, ye, ym) chi2.__doc__ = _chi2_np.__doc__ _soft_l1_loss_np = _soft_l1_loss _soft_l1_loss_nb = jit( nogil=True, cache=True, error_model="numpy", )(_soft_l1_loss_np) def _soft_l1_loss(z_sqr): if z_sqr.dtype in (np.float32, np.float64): return _soft_l1_loss_nb(z_sqr) # fallback to numpy for float128 return _soft_l1_loss_np(z_sqr) @nb_overload(_soft_l1_loss, inline="always") def _ol_soft_l1_loss(z_sqr): return _soft_l1_loss_np # pragma: no cover _soft_l1_cost_np = _soft_l1_cost _soft_l1_cost_nb = jit( nogil=True, cache=True, error_model="numpy", )(_soft_l1_cost_np) def _soft_l1_cost(y, ye, ym): if ym.dtype in (np.float32, np.float64): return _soft_l1_cost_nb(y, ye, ym) # fallback to numpy for float128 return _soft_l1_cost_np(y, ye, ym) except ModuleNotFoundError: pass class Cost(abc.ABC): """ Base class for all cost functions. :meta private: """ __slots__ = ("_parameters", "_verbose") _parameters: Dict[str, Optional[Tuple[float, float]]] _verbose: int @property def errordef(self): """ For internal use. :meta private: """ return self._errordef() def _errordef(self): return CHISQUARE @property def ndata(self): """ Return number of points in least-squares fits or bins in a binned fit. Infinity is returned if the cost function is unbinned. This is used by Minuit to compute the reduced chi2, a goodness-of-fit estimate. """ return self._ndata() @abc.abstractmethod def _ndata(self): NotImplemented # pragma: no cover @property def verbose(self): """ Access verbosity level. Set this to 1 to print all function calls with input and output. """ return self._verbose @verbose.setter def verbose(self, value: int): self._verbose = value def __init__( self, parameters: Dict[str, Optional[Tuple[float, float]]], verbose: int ): """For internal use.""" self._parameters = parameters self._verbose = verbose def __add__(self, rhs): """ Add two cost functions to form a combined cost function. Returns ------- CostSum """ return CostSum(self, rhs) def __radd__(self, lhs): """ Add two cost functions to form a combined cost function. Returns ------- CostSum """ return CostSum(lhs, self) def __call__(self, *args: float) -> float: """ Evaluate the cost function. If verbose >= 1, print arguments and result. Parameters ---------- *args : float Parameter values. Returns ------- float """ r = self._call(args) if self.verbose >= 1: print(args, "->", r) return r @abc.abstractmethod def _call(self, args: Sequence[float]) -> float: ... # pragma: no cover class Constant(Cost): """ Cost function that represents a constant. If your cost function produces results that are far away from O(1), adding a constant that brings the value closer to zero may improve the numerical stability. """ __slots__ = "value" def __init__(self, value: float): """Initialize constant with a value.""" self.value = value super().__init__({}, False) def _ndata(self): return 0 def _call(self, args: Sequence[float]) -> float: return self.value class CostSum(Cost, ABCSequence): """ Sum of cost functions. Users do not need to create objects of this class themselves. They should just add cost functions, for example:: nll = UnbinnedNLL(...) lsq = LeastSquares(...) ncs = NormalConstraint(...) csum = nll + lsq + ncs CostSum is used to combine data from different experiments or to combine normal cost functions with penalty terms (see NormalConstraint). The parameters of CostSum are the union of all parameters of its constituents. Supports the sequence protocol to access the constituents. Warnings -------- CostSum does not support cost functions that accept a parameter array, because the function signature does not allow one to determine how many parameters are accepted by the function and which parameters overlap between different cost functions. """ __slots__ = "_items", "_maps" def __init__(self, *items: Union[Cost, float]): """ Initialize with cost functions. Parameters ---------- *items : Cost Cost functions. May also be other CostSum functions. """ self._items: List[Cost] = [] for item in items: if isinstance(item, CostSum): self._items += item._items elif isinstance(item, (int, float)): if item != 0: self._items.append(Constant(item)) else: self._items.append(item) signatures, self._maps = merge_signatures(self._items, annotations=True) super().__init__(signatures, max(c.verbose for c in self._items)) def _split(self, args: Sequence[float]): for component, cmap in zip(self._items, self._maps): component_args = tuple(args[i] for i in cmap) yield component, component_args def _call(self, args: Sequence[float]) -> float: r = 0.0 for comp, cargs in self._split(args): r += comp._call(cargs) / comp.errordef return r def _ndata(self): return sum(c.ndata for c in self._items) def __len__(self): """Return number of constituent cost functions.""" return self._items.__len__() def __getitem__(self, key): """Get constituent cost function by index.""" return self._items.__getitem__(key) def visualize( self, args: Sequence[float], component_kwargs: Dict[int, Dict[str, Any]] = None ): """ Visualize data and model agreement (requires matplotlib). The visualization is drawn with matplotlib.pyplot into the current figure. Subplots are created to visualize each part of the cost function, the figure height is increased accordingly. Parts without a visualize method are silently ignored. Parameters ---------- args : array-like Parameter values. component_kwargs : dict of dicts, optional Dict that maps an index to dict of keyword arguments. This can be used to pass keyword arguments to a visualize method of a component with that index. **kwargs : Other keyword arguments are forwarded to all components. """ from matplotlib import pyplot as plt n = sum(hasattr(comp, "visualize") for comp in self) fig = plt.gcf() fig.set_figwidth(n * fig.get_figwidth() / 1.5) _, ax = plt.subplots(1, n, num=fig.number) if component_kwargs is None: component_kwargs = {} i = 0 for k, (comp, cargs) in enumerate(self._split(args)): if not hasattr(comp, "visualize"): continue kwargs = component_kwargs.get(k, {}) plt.sca(ax[i]) comp.visualize(cargs, **kwargs) i += 1 class MaskedCost(Cost): """ Base class for cost functions that support data masking. :meta private: """ __slots__ = "_data", "_mask", "_masked" _mask: Optional[NDArray] def __init__( self, args: Dict[str, Optional[Tuple[float, float]]], data: NDArray, verbose: int, ): """For internal use.""" self._data = data self._mask = None self._update_cache() Cost.__init__(self, args, verbose) @property def mask(self): """ Boolean array, array of indices, or None. If not None, only values selected by the mask are considered. The mask acts on the first dimension of a value array, i.e. values[mask]. Default is None. """ return self._mask @mask.setter def mask(self, mask: Optional[ArrayLike]): self._mask = None if mask is None else np.asarray(mask) self._update_cache() @property def data(self): """Return data samples.""" return self._data @data.setter def data(self, value: ArrayLike): self._data[...] = value self._update_cache() def _update_cache(self): self._masked = self._data[_replace_none(self._mask, ...)] class UnbinnedCost(MaskedCost): """ Base class for unbinned cost functions. :meta private: """ __slots__ = "_model", "_log" def __init__(self, data, model: Model, verbose: int, log: bool): """For internal use.""" self._model = model self._log = log super().__init__(_model_parameters(model), _norm(data), verbose) @abc.abstractproperty def pdf(self): """Get probability density model.""" ... # pragma: no cover @abc.abstractproperty def scaled_pdf(self): """Get number density model.""" ... # pragma: no cover def _ndata(self): # unbinned likelihoods have infinite degrees of freedom return np.inf @deprecated_parameter(bins="nbins") def visualize( self, args: Sequence[float], model_points: Union[int, Sequence[float]] = 0, bins: int = 50, ): """ Visualize data and model agreement (requires matplotlib). The visualization is drawn with matplotlib.pyplot into the current axes. Parameters ---------- args : array-like Parameter values. model_points : int or array-like, optional How many points to use to draw the model. Default is 0, in this case an smart sampling algorithm selects the number of points. If array-like, it is interpreted as the point locations. bins : int, optional number of bins. Default is 50 bins. """ from matplotlib import pyplot as plt x = np.sort(self.data) if x.ndim > 1: raise ValueError("visualize is not implemented for multi-dimensional data") # this implementation only works with a histogram with linear spacing if isinstance(model_points, Iterable): xm = np.array(model_points) ym = self.scaled_pdf(xm, *args) elif model_points > 0: if _detect_log_spacing(x): xm = np.geomspace(x[0], x[-1], model_points) else: xm = np.linspace(x[0], x[-1], model_points) ym = self.scaled_pdf(xm, *args) else: xm, ym = _smart_sampling(lambda x: self.scaled_pdf(x, *args), x[0], x[-1]) # use xm for range, which may be narrower or wider than x range n, xe = np.histogram(x, bins=bins, range=(xm[0], xm[-1])) cx = 0.5 * (xe[1:] + xe[:-1]) dx = xe[1] - xe[0] plt.errorbar(cx, n, n**0.5, fmt="ok") plt.fill_between(xm, 0, ym * dx, fc="C0") class UnbinnedNLL(UnbinnedCost): """ Unbinned negative log-likelihood. Use this if only the shape of the fitted PDF is of interest and the original unbinned data is available. The data can be one- or multi-dimensional. """ __slots__ = () @property def pdf(self): """Get probability density model.""" if self._log: return lambda *args: np.exp(self._model(*args)) return self._model @property def scaled_pdf(self): """Get number density model.""" scale = np.prod(self.data.shape) if self._log: return lambda *args: scale * np.exp(self._model(*args)) return lambda *args: scale * self._model(*args) def __init__( self, data: ArrayLike, pdf: Model, verbose: int = 0, log: bool = False, ): """ Initialize UnbinnedNLL with data and model. Parameters ---------- data : array-like Sample of observations. If the observations are multidimensional, data must have the shape (D, N), where D is the number of dimensions and N the number of data points. pdf : callable Probability density function of the form f(data, par0, [par1, ...]), where data is the data sample and par0, ... are model parameters. If the data are multivariate, data passed to f has shape (D, N), where D is the number of dimensions and N the number of data points. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. log : bool, optional Distributions of the exponential family (normal, exponential, poisson, ...) allow one to compute the logarithm of the pdf directly, which is more accurate and efficient than effectively doing ``log(exp(logpdf))``. Set this to True, if the model returns the logarithm of the pdf instead of the pdf. Default is False. """ super().__init__(data, pdf, verbose, log) def _call(self, args: Sequence[float]) -> float: data = self._masked x = self._model(data, *args) x = _normalize_model_output(x) if self._log: return -2.0 * np.sum(x) return 2.0 * _unbinned_nll(x) class ExtendedUnbinnedNLL(UnbinnedCost): """ Unbinned extended negative log-likelihood. Use this if shape and normalization of the fitted PDF are of interest and the original unbinned data is available. The data can be one- or multi-dimensional. """ __slots__ = () @property def pdf(self): """Get probability density model.""" if self._log: def fn(*args): n, x = self._model(*args) return np.exp(x) / n else: def fn(*args): n, x = self._model(*args) return x / n return fn @property def scaled_pdf(self): """Get density model.""" if self._log: return lambda *args: np.exp(self._model(*args)[1]) return lambda *args: self._model(*args)[1] def __init__( self, data: ArrayLike, scaled_pdf: Model, verbose: int = 0, log: bool = False, ): """ Initialize cost function with data and model. Parameters ---------- data : array-like Sample of observations. If the observations are multidimensional, data must have the shape (D, N), where D is the number of dimensions and N the number of data points. scaled_pdf : callable Density function of the form f(data, par0, [par1, ...]), where data is the sample and par0, ... are model parameters. Must return a tuple (, ). The first value is the density integrated over the data window, the interval that we consider for the fit. For example, if the data are exponentially distributed, but we fit only the interval (0, 5), then the first value is the density integrated from 0 to 5. If the data are multivariate, data passed to f has shape (D, N), where D is the number of dimensions and N the number of data points. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. log : bool, optional Distributions of the exponential family (normal, exponential, poisson, ...) allow one to compute the logarithm of the pdf directly, which is more accurate and efficient than effectively doing ``log(exp(logpdf))``. Set this to True, if the model returns the logarithm of the density as the second argument instead of the density. Default is False. """ super().__init__(data, scaled_pdf, verbose, log) def _call(self, args: Sequence[float]) -> float: data = self._masked ns, x = self._model(data, *args) x = _normalize_model_output( x, "Model should return numpy array in second position" ) if self._log: return 2 * (ns - np.sum(x)) return 2 * (ns + _unbinned_nll(x)) class BinnedCost(MaskedCost): """ Base class for binned cost functions. :meta private: """ __slots__ = "_xe", "_ndim", "_bztrafo" _xe: Union[NDArray, Tuple[NDArray, ...]] _ndim: int _bztrafo: Optional[BohmZechTransform] n = MaskedCost.data @property def xe(self): """Access bin edges.""" return self._xe def __init__( self, parameters: Dict[str, Optional[Tuple[float, float]]], n: ArrayLike, xe: Union[ArrayLike, Sequence[ArrayLike]], verbose: int, *updater, ): """For internal use.""" if not isinstance(xe, Iterable): raise ValueError("xe must be iterable") shape = _shape_from_xe(xe) self._ndim = len(shape) if self._ndim == 1: self._xe = _norm(xe) # type:ignore else: self._xe = tuple(_norm(xei) for xei in xe) n = _norm(n) is_weighted = n.ndim > self._ndim if n.ndim != (self._ndim + int(is_weighted)): raise ValueError("n must either have same dimension as xe or one extra") xei: NDArray for i, xei in enumerate([self._xe] if self._ndim == 1 else self._xe): if len(xei) != n.shape[i] + 1: raise ValueError( f"n and xe have incompatible shapes along dimension {i}, " "xe must be longer by one element along each dimension" ) if is_weighted: if n.shape[-1] != 2: raise ValueError("n must have shape (..., 2)") self._bztrafo = BohmZechTransform(n[..., 0], n[..., 1]) else: self._bztrafo = None super().__init__(parameters, n, verbose) def prediction( self, args: Sequence[float] ) -> Union[NDArray, Tuple[NDArray, NDArray]]: """ Return the bin expectation for the fitted model. Parameters ---------- args : array-like Parameter values. Returns ------- NDArray Model prediction for each bin. """ return self._pred(args) def pulls(self, args: Sequence[float]) -> NDArray: """ Return studentized residuals (aka pulls). Parameters ---------- args : sequence of float Parameter values. Returns ------- array Array of pull values. If the cost function is masked, the array contains NaN values where the mask value is False. Notes ----- Pulls allow one to estimate how well a model fits the data. A pull is a value computed for each data bin. It is given by (observed - predicted) / standard-deviation. If the model is correct, the expectation value of each pull is zero and its variance is one in the asymptotic limit of infinite samples. Under these conditions, the chi-square statistic is computed from the sum of pulls squared has a known probability distribution if the model is correct. It therefore serves as a goodness-of-fit statistic. Beware: the sum of pulls squared in general is not identical to the value returned by the cost function, even if the cost function returns a chi-square distributed test-statistic. The cost function is computed in a slightly differently way that makes the return value approach the asymptotic chi-square distribution faster than a test statistic based on sum of pulls squared. In summary, only use pulls for plots. Compute the chi-square test statistic directly from the cost function. """ return self._pulls(args) def visualize(self, args: Sequence[float]) -> None: """ Visualize data and model agreement (requires matplotlib). The visualization is drawn with matplotlib.pyplot into the current axes. Parameters ---------- args : sequence of float Parameter values. Notes ----- The automatically provided visualization for multi-dimensional data set is often not very pretty, but still helps to judge whether the fit is reasonable. Since there is no obvious way to draw higher dimensional data with error bars in comparison to a model, the visualization shows all data bins as a single sequence. """ return self._vis(args) def _vis(self, args: Sequence[float]) -> None: from matplotlib import pyplot as plt n, ne = self._n_err() mu = self.prediction(args) assert not isinstance(mu, tuple) if self._ndim > 1: # flatten higher-dimensional data n = n.reshape(-1) ne = ne.reshape(-1) mu = mu.reshape(-1) # just use bin numbers instead of original values xe = np.arange(len(n) + 1) - 0.5 cx = np.arange(len(n)).astype(float) else: xe = self.xe cx = 0.5 * (xe[1:] + xe[:-1]) plt.errorbar(cx, n, ne, fmt="ok") plt.stairs(mu, xe, fill=True, color="C0") @abc.abstractmethod def _pred(self, args: Sequence[float]) -> Union[NDArray, Tuple[NDArray, NDArray]]: ... # pragma: no cover def _ndata(self): return np.prod(self._masked.shape[: self._ndim]) def _update_cache(self): super()._update_cache() if self._bztrafo: ma = _replace_none(self._mask, ...) self._bztrafo = BohmZechTransform(self._data[ma, 0], self._data[ma, 1]) def _n_err(self) -> Tuple[NDArray, NDArray]: d = self.data if self._bztrafo: n = d[..., 0].copy() err = d[..., 1] ** 0.5 else: n = d.copy() err = d**0.5 if self.mask is not None: ma = ~self.mask n[ma] = np.nan err[ma] = np.nan # mask values where error is zero ma = err == 0 err[ma] = np.nan n[ma] = np.nan return n, err def _pulls(self, args: Sequence[float]) -> NDArray: mu = self.prediction(args) n, ne = self._n_err() return (n - mu) / ne class BinnedCostWithModel(BinnedCost): """ Base class for binned cost functions with parametric model. :meta private: """ __slots__ = "_xe_shape", "_model", "_model_xe" _model_xe: np.ndarray _xe_shape: Union[Tuple[int], Tuple[int, ...]] def __init__(self, n, xe, model, verbose): """For internal use.""" self._model = model super().__init__(_model_parameters(model), n, xe, verbose) if self._ndim == 1: self._xe_shape = (len(self.xe),) self._model_xe = _norm(self.xe) else: self._xe_shape = tuple(len(xei) for xei in self.xe) self._model_xe = np.row_stack( [x.flatten() for x in np.meshgrid(*self.xe, indexing="ij")] ) def _pred(self, args: Sequence[float]) -> NDArray: d = self._model(self._model_xe, *args) d = _normalize_model_output(d) expected_shape = (np.prod(self._xe_shape),) if d.shape != expected_shape: raise ValueError( f"Expected model to return an array of shape {expected_shape}, " f"but it returns an array of shape {d.shape}" ) if self._ndim > 1: d = d.reshape(self._xe_shape) for i in range(self._ndim): d = np.diff(d, axis=i) # differences can come out negative due to round-off error in subtraction, # we set negative values to zero d[d < 0] = 0 return d class Template(BinnedCost): """ Binned cost function for a template fit with uncertainties on the template. This cost function is for a mixture of components. Use this if the sample originate from two or more components and you are interested in estimating the yield that originates from one or more components. In high-energy physics, one component is often a peaking signal over a smooth background component. A component can be described by a parametric model or a template. A parametric model is accepted in form of a scaled cumulative density function, while a template is a non-parametric shape estimate obtained by histogramming a Monte-Carlo simulation. Even if the Monte-Carlo simulation is asymptotically correct, estimating the shape from a finite simulation sample introduces some uncertainty. This cost function takes that additional uncertainty into account. There are several ways to fit templates and take the sampling uncertainty into account. Barlow and Beeston [1]_ found an exact likelihood for this problem, with one nuisance parameter per component per bin. Solving this likelihood is somewhat challenging though. The Barlow-Beeston likelihood also does not handle the additional uncertainty in weighted templates unless the weights per bin are all equal. Other works [2]_ [3]_ [4]_ describe likelihoods that use only one nuisance parameter per bin, which is an approximation. Some marginalize over the nuisance parameters with some prior, while others profile over the nuisance parameter. This class implements several of these methods. The default method is the one which performs best under most conditions, according to current knowledge. The default may change if this assessment changes. The cost function returns an asymptotically chi-square distributed test statistic, except for the method "asy", where it is the negative logarithm of the marginalised likelihood instead. The standard transform [5]_ which we use convert likelihoods into test statistics only works for (profiled) likelihoods, not for likelihoods marginalized over a prior. All methods implemented here have been generalized to work with both weighted data and weighted templates, under the assumption that the weights are independent of the data. This is not the case for sWeights, and the uncertaintes for results obtained with sWeights will only be approximately correct [6]_. The methods have been further generalized to allow fitting a mixture of parametric models and templates. .. [1] Barlow and Beeston, Comput.Phys.Commun. 77 (1993) 219-228 .. [2] Conway, PHYSTAT 2011 proceeding, https://doi.org/10.48550/arXiv.1103.0354 .. [3] Argüelles, Schneider, Yuan, JHEP 06 (2019) 030 .. [4] Dembinski and Abdelmotteleb, https://doi.org/10.48550/arXiv.2206.12346 .. [5] Baker and Cousins, NIM 221 (1984) 437-442 .. [6] Langenbruch, Eur.Phys.J.C 82 (2022) 5, 393 """ __slots__ = "_model_data", "_model_xe", "_xe_shape", "_impl" _model_data: List[ Union[ Tuple[NDArray, NDArray], Tuple[Model, float], ] ] _model_xe: np.ndarray _xe_shape: Union[Tuple[int], Tuple[int, ...]] def __init__( self, n: ArrayLike, xe: Union[ArrayLike, Sequence[ArrayLike]], model_or_template: Collection[Union[Model, ArrayLike]], name: Optional[Sequence[str]] = None, verbose: int = 0, method: str = "da", ): """ Initialize cost function with data and model. Parameters ---------- n : array-like Histogram counts. If this is an array with dimension D+1, where D is the number of histogram axes, then the last dimension must have two elements and is interpreted as pairs of sum of weights and sum of weights squared. xe : array-like or collection of array-like Bin edge locations, must be len(n) + 1, where n is the number of bins. If the histogram has more than one axis, xe must be a collection of the bin edge locations along each axis. model_or_template : collection of array-like or callable Collection of models or arrays. An array represent the histogram counts of a template. The template histograms must use the same axes as the data histogram. If the counts are represented by an array with dimension D+1, where D is the number of histogram axes, then the last dimension must have two elements and is interpreted as pairs of sum of weights and sum of weights squared. Callables must return the model cdf evaluated as xe. name : collection of str, optional Optional name for the yield of each template and the parameter of each model (in order). Must have the same length as there are templates and model parameters in templates_or_model. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. method : {"jsc", "asy", "da"}, optional Which method to use. "jsc": Conway's method [2]_. "asy": ASY method [3]_. "da": DA method [4]_. Default is "da", which to current knowledge offers the best overall performance. The default may change in the future, so please set this parameter explicitly in code that has to be stable. For all methods except the "asy" method, the minimum value is chi-square distributed. """ M = len(model_or_template) if M < 1: raise ValueError("at least one template or model is required") shape = _shape_from_xe(xe) ndim = len(shape) npar = 0 annotated: Dict[str, Optional[Tuple[float, float]]] = {} self._model_data = [] for i, t in enumerate(model_or_template): if isinstance(t, Collection): tt = _norm(t) if tt.ndim > ndim: # template is weighted if tt.ndim != ndim + 1 or tt.shape[:-1] != shape: raise ValueError("shapes of n and templates do not match") t1 = tt[..., 0].copy() t2 = tt[..., 1].copy() else: if tt.ndim != ndim or tt.shape != shape: raise ValueError("shapes of n and templates do not match") t1 = tt.copy() t2 = tt.copy() # normalize to unity f = 1 / np.sum(t1) t1 *= f t2 *= f**2 self._model_data.append((t1, t2)) annotated[f"x{i}"] = (0.0, np.inf) elif isinstance(t, Model): ann = _model_parameters(t) npar = len(ann) self._model_data.append((t, npar)) for k in ann: annotated[f"x{i}_{k}"] = ann[k] else: raise ValueError( "model_or_template must be a collection of array-likes " "and/or Model types" ) if name is not None: if len(annotated) != len(name): raise ValueError( "number of names must match number of templates and " "model parameters" ) annotated = {new: annotated[old] for (old, new) in zip(annotated, name)} known_methods = { "jsc": template_chi2_jsc, "asy": template_nll_asy, "hpd": template_chi2_da, "da": template_chi2_da, } try: self._impl = known_methods[method] except KeyError: raise ValueError( f"method {method} is not understood, allowed values: {known_methods}" ) if method == "hpd": warnings.warn( "key 'hpd' is deprecated, please use 'da' instead", category=np.VisibleDeprecationWarning, stacklevel=2, ) super().__init__(annotated, n, xe, verbose) if self._ndim == 1: self._xe_shape = (len(self.xe),) self._model_xe = _norm(self.xe) else: self._xe_shape = tuple(len(xei) for xei in self.xe) self._model_xe = np.row_stack( [x.flatten() for x in np.meshgrid(*self.xe, indexing="ij")] ) def _pred(self, args: Sequence[float]) -> Tuple[NDArray, NDArray]: mu: NDArray = 0 # type:ignore mu_var: NDArray = 0 # type:ignore i = 0 for t1, t2 in self._model_data: if isinstance(t1, np.ndarray) and isinstance(t2, np.ndarray): a = args[i] mu += a * t1 mu_var += a**2 * t2 i += 1 elif isinstance(t1, Model) and isinstance(t2, int): d = t1(self._model_xe, *args[i : i + t2]) d = _normalize_model_output(d) if self._ndim > 1: d = d.reshape(self._xe_shape) for j in range(self._ndim): d = np.diff(d, axis=j) # differences can come out negative due to round-off error in # subtraction, we set negative values to zero d[d < 0] = 0 mu += d mu_var += np.ones_like(mu) * 1e-300 i += t2 else: # never arrive here assert False # pragma: no cover return mu, mu_var def _call(self, args: Sequence[float]) -> float: mu, mu_var = self._pred(args) ma = self.mask if ma is not None: mu = mu[ma] mu_var = mu_var[ma] if self._bztrafo: n, mu, mu_var = self._bztrafo(mu, mu_var) else: n = self._masked ma = mu > 0 return self._impl(n[ma], mu[ma], mu_var[ma]) def _errordef(self) -> float: return NEGATIVE_LOG_LIKELIHOOD if self._impl is template_nll_asy else CHISQUARE def prediction(self, args: Sequence[float]) -> Tuple[NDArray, NDArray]: """ Return the fitted template and its standard deviation. This returns the prediction from the templates, the sum over the products of the template yields with the normalized templates. The standard deviation is returned as the second argument, this is the estimated uncertainty of the fitted template alone. It is obtained via error propagation, taking the statistical uncertainty in the template into account, but regarding the yields as parameters without uncertainty. Parameters ---------- args : array-like Parameter values. Returns ------- y, yerr : NDArray, np.array Template prediction and its standard deviation, based on the statistical uncertainty of the template only. """ mu, mu_var = self._pred(args) return mu, np.sqrt(mu_var) def _vis(self, args: Sequence[float]) -> None: from matplotlib import pyplot as plt n, ne = self._n_err() mu, mue = self.prediction(args) # type: ignore # see implementation notes in BinnedCost.visualize if self._ndim > 1: n = n.reshape(-1) ne = ne.reshape(-1) mu = mu.reshape(-1) mue = mue.reshape(-1) xe = np.arange(len(n) + 1) - 0.5 cx = np.arange(len(n)).astype(float) else: xe = self.xe cx = 0.5 * (xe[1:] + xe[:-1]) plt.errorbar(cx, n, ne, fmt="ok") # need fill=True and fill=False so that bins with mue=0 show up for fill in (False, True): plt.stairs(mu + mue, xe, baseline=mu - mue, fill=fill, color="C0") def _pulls(self, args: Sequence[float]) -> NDArray: mu, mue = self.prediction(args) n, ne = self._n_err() return (n - mu) / (mue**2 + ne**2) ** 0.5 class BinnedNLL(BinnedCostWithModel): """ Binned negative log-likelihood. Use this if only the shape of the fitted PDF is of interest and the data is binned. This cost function works with normal and weighted histograms. The histogram can be one- or multi-dimensional. The cost function has a minimum value that is asymptotically chi2-distributed. It is constructed from the log-likelihood assuming a multivariate-normal distribution and using the saturated model as a reference. """ __slots__ = () @property def cdf(self): """Get cumulative density function.""" return self._model def __init__( self, n: ArrayLike, xe: Union[ArrayLike, Sequence[ArrayLike]], cdf: Model, verbose: int = 0, ): """ Initialize cost function with data and model. Parameters ---------- n : array-like Histogram counts. If this is an array with dimension D+1, where D is the number of histogram axes, then the last dimension must have two elements and is interpreted as pairs of sum of weights and sum of weights squared. xe : array-like or collection of array-like Bin edge locations, must be len(n) + 1, where n is the number of bins. If the histogram has more than one axis, xe must be a collection of the bin edge locations along each axis. cdf : callable Cumulative density function of the form f(xe, par0, par1, ..., parN), where xe is a bin edge and par0, ... are model parameters. The corresponding density must be normalized to unity over the space covered by the histogram. If the model is multivariate, xe must be an array-like with shape (D, N), where D is the dimension and N is the number of points where the model is evaluated. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. """ super().__init__(n, xe, cdf, verbose) def _pred(self, args: Sequence[float]) -> NDArray: p = super()._pred(args) ma = self.mask if ma is not None: p /= np.sum(p[ma]) # normalise probability of remaining bins scale = np.sum(self._masked[..., 0] if self._bztrafo else self._masked) return p * scale def _call(self, args: Sequence[float]) -> float: mu = self._pred(args) ma = self.mask if ma is not None: mu = mu[ma] if self._bztrafo: n, mu = self._bztrafo(mu) else: n = self._masked return multinominal_chi2(n, mu) class ExtendedBinnedNLL(BinnedCostWithModel): """ Binned extended negative log-likelihood. Use this if shape and normalization of the fitted PDF are of interest and the data is binned. This cost function works with normal and weighted histograms. The histogram can be one- or multi-dimensional. The cost function works for both weighted data. The cost function assumes that the weights are independent of the data. This is not the case for sWeights, and the uncertaintes for results obtained with sWeights will only be approximately correct, see C. Langenbruch, Eur.Phys.J.C 82 (2022) 5, 393. The cost function has a minimum value that is asymptotically chi2-distributed. It is constructed from the log-likelihood assuming a poisson distribution and using the saturated model as a reference. """ __slots__ = () @property def scaled_cdf(self): """Get integrated density model.""" return self._model def __init__( self, n: ArrayLike, xe: Union[ArrayLike, Sequence[ArrayLike]], scaled_cdf: Model, verbose: int = 0, ): """ Initialize cost function with data and model. Parameters ---------- n : array-like Histogram counts. If this is an array with dimension D+1, where D is the number of histogram axes, then the last dimension must have two elements and is interpreted as pairs of sum of weights and sum of weights squared. xe : array-like or collection of array-like Bin edge locations, must be len(n) + 1, where n is the number of bins. If the histogram has more than one axis, xe must be a collection of the bin edge locations along each axis. scaled_cdf : callable Scaled Cumulative density function of the form f(xe, par0, [par1, ...]), where xe is a bin edge and par0, ... are model parameters. If the model is multivariate, xe must be an array-like with shape (D, N), where D is the dimension and N is the number of points where the model is evaluated. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. """ super().__init__(n, xe, scaled_cdf, verbose) def _call(self, args: Sequence[float]) -> float: mu = self._pred(args) ma = self.mask if ma is not None: mu = mu[ma] if self._bztrafo: n, mu = self._bztrafo(mu) else: n = self._masked # assert isinstance(n, np.ndarray) return poisson_chi2(n, mu) class LeastSquares(MaskedCost): """ Least-squares cost function (aka chisquare function). Use this if you have data of the form (x, y +/- yerror), where x can be one-dimensional or multi-dimensional, but y is always one-dimensional. See :meth:`__init__` for details on how to use a multivariate model. """ __slots__ = "_loss", "_cost", "_model", "_ndim" _loss: Union[str, LossFunction] @property def x(self): """Get explanatory variables.""" if self._ndim == 1: return self.data[:, 0] return self.data.T[: self._ndim] @x.setter def x(self, value): if self._ndim == 1: self.data[:, 0] = _norm(value) else: self.data[:, : self._ndim] = _norm(value).T self._update_cache() @property def y(self): """Get samples.""" return self.data[:, self._ndim] @y.setter def y(self, value): self.data[:, self._ndim] = _norm(value) self._update_cache() @property def yerror(self): """Get sample uncertainties.""" return self.data[:, self._ndim + 1] @yerror.setter def yerror(self, value): self.data[:, self._ndim + 1] = _norm(value) self._update_cache() @property def model(self): """Get model of the form y = f(x, par0, [par1, ...]).""" return self._model @property def loss(self): """Get loss function.""" return self._loss @loss.setter def loss(self, loss: Union[str, LossFunction]): self._loss = loss if isinstance(loss, str): if loss == "linear": self._cost = chi2 elif loss == "soft_l1": self._cost = _soft_l1_cost else: raise ValueError(f"unknown loss {loss!r}") elif isinstance(loss, LossFunction): self._cost = lambda y, ye, ym: np.sum( loss(_z_squared(y, ye, ym)) # type:ignore ) else: raise ValueError("loss must be str or LossFunction") def __init__( self, x: ArrayLike, y: ArrayLike, yerror: ArrayLike, model: Model, loss: Union[str, LossFunction] = "linear", verbose: int = 0, ): """ Initialize cost function with data and model. Parameters ---------- x : array-like Locations where the model is evaluated. If the model is multivariate, x must have shape (D, N), where D is the number of dimensions and N the number of data points. y : array-like Observed values. Must have the same length as x. yerror : array-like or float Estimated uncertainty of observed values. Must have same shape as y or be a scalar, which is then broadcasted to same shape as y. model : callable Function of the form f(x, par0, [par1, ...]) whose output is compared to observed values, where x is the location and par0, ... are model parameters. If the model is multivariate, x has shape (D, N), where D is the number of dimensions and N the number of data points. loss : str or callable, optional The loss function can be modified to make the fit robust against outliers, see scipy.optimize.least_squares for details. Only "linear" (default) and "soft_l1" are currently implemented, but users can pass any loss function as this argument. It should be a monotonic, twice differentiable function, which accepts the squared residual and returns a modified squared residual. verbose : int, optional Verbosity level. 0: is no output (default). 1: print current args and negative log-likelihood value. Notes ----- Alternative loss functions make the fit more robust against outliers by weakening the pull of outliers. The mechanical analog of a least-squares fit is a system with attractive forces. The points pull the model towards them with a force whose potential is given by :math:`rho(z)` for a squared-offset :math:`z`. The plot shows the standard potential in comparison with the weaker soft-l1 potential, in which outliers act with a constant force independent of their distance. .. plot:: plots/loss.py """ x = _norm(x) y = _norm(y) assert x.ndim >= 1 # guaranteed by _norm self._ndim = x.ndim self._model = model self.loss = loss x = np.atleast_2d(x) data = np.column_stack(np.broadcast_arrays(*x, y, yerror)) super().__init__(_model_parameters(self._model), data, verbose) def _call(self, args: Sequence[float]) -> float: x = self._masked.T[0] if self._ndim == 1 else self._masked.T[: self._ndim] y, yerror = self._masked.T[self._ndim :] ym = self._model(x, *args) ym = _normalize_model_output(ym) return self._cost(y, yerror, ym) def _ndata(self): return len(self._masked) def visualize(self, args: ArrayLike, model_points: Union[int, Sequence[float]] = 0): """ Visualize data and model agreement (requires matplotlib). The visualization is drawn with matplotlib.pyplot into the current axes. Parameters ---------- args : array-like Parameter values. model_points : int or array-like, optional How many points to use to draw the model. Default is 0, in this case an smart sampling algorithm selects the number of points. If array-like, it is interpreted as the point locations. """ from matplotlib import pyplot as plt if self._ndim > 1: raise ValueError("visualize is not implemented for multi-dimensional data") x, y, ye = self._masked.T plt.errorbar(x, y, ye, fmt="ok") if isinstance(model_points, Iterable): xm = np.array(model_points) ym = self.model(xm, *args) elif model_points > 0: if _detect_log_spacing(x): xm = np.geomspace(x[0], x[-1], model_points) else: xm = np.linspace(x[0], x[-1], model_points) ym = self.model(xm, *args) else: xm, ym = _smart_sampling(lambda x: self.model(x, *args), x[0], x[-1]) plt.plot(xm, ym) def prediction(self, args: Sequence[float]) -> NDArray: """ Return the prediction from the fitted model. Parameters ---------- args : array-like Parameter values. Returns ------- NDArray Model prediction for each bin. """ return self.model(self.x, *args) def pulls(self, args: Sequence[float]) -> NDArray: # noqa: D102 y = self.y.copy() ye = self.yerror.copy() ym = self.prediction(args) if self.mask is not None: ma = ~self.mask y[ma] = np.nan ye[ma] = np.nan return (y - ym) / ye LeastSquares.pulls.__doc__ = BinnedCost.pulls.__doc__ class NormalConstraint(Cost): """ Gaussian penalty for one or several parameters. The Gaussian penalty acts like a pseudo-measurement of the parameter itself, based on a (multi-variate) normal distribution. Penalties can be set for one or several parameters at once (which is more efficient). When several parameter are constrained, one can specify the full covariance matrix of the parameters. Notes ----- It is sometimes necessary to add a weak penalty on a parameter to avoid instabilities in the fit. A typical example in high-energy physics is the fit of a signal peak above some background. If the amplitude of the peak vanishes, the shape parameters of the peak become unconstrained and the fit becomes unstable. This can be avoided by adding weak (large uncertainty) penalty on the shape parameters whose pull is negligible if the peak amplitude is non-zero. This class can also be used to approximately include external measurements of some parameters, if the original cost function is not available or too costly to compute. If the external measurement was performed in the asymptotic limit with a large sample, a Gaussian penalty is an accurate statistical representation of the external result. """ __slots__ = "_value", "_cov", "_covinv" def __init__( self, args: Union[str, Iterable[str]], value: ArrayLike, error: ArrayLike, ): """ Initialize the normal constraint with expected value(s) and error(s). Parameters ---------- args : str or sequence of str Parameter name(s). value : float or array-like Expected value(s). Must have same length as `args`. error : float or array-like Expected error(s). If 1D, must have same length as `args`. If 2D, must be the covariance matrix of the parameters. """ self._value = _norm(value) self._cov = _norm(error) if self._cov.ndim < 2: self._cov **= 2 self._covinv = _covinv(self._cov) tp_args = (args,) if isinstance(args, str) else tuple(args) super().__init__({k: None for k in tp_args}, False) @property def covariance(self): """ Get expected covariance of parameters. Can be 1D (diagonal of covariance matrix) or 2D (full covariance matrix). """ return self._cov @covariance.setter def covariance(self, value): self._cov[:] = value self._covinv = _covinv(self._cov) @property def value(self): """Get expected parameter values.""" return self._value @value.setter def value(self, value): self._value[:] = value def _call(self, args: Sequence[float]) -> float: delta = self._value - args if self._covinv.ndim < 2: return np.sum(delta**2 * self._covinv) return np.einsum("i,ij,j", delta, self._covinv, delta) def _ndata(self): return len(self._value) def visualize(self, args: ArrayLike): """ Visualize data and model agreement (requires matplotlib). The visualization is drawn with matplotlib.pyplot into the current axes. Parameters ---------- args : array-like Parameter values. """ from matplotlib import pyplot as plt args = np.atleast_1d(args) par = self._parameters val = self.value cov = self.covariance if cov.ndim == 2: cov = np.diag(cov) err = np.sqrt(cov) n = len(par) i = 0 max_pull = 0 for v, e, a in zip(val, err, args): pull = (a - v) / e max_pull = max(abs(pull), max_pull) plt.errorbar(pull, -i, 0, 1, fmt="o", color="C0") i += 1 plt.axvline(0, color="k") plt.xlim(-max_pull - 1.1, max_pull + 1.1) yaxis = plt.gca().yaxis yaxis.set_ticks(-np.arange(n)) yaxis.set_ticklabels(par) plt.ylim(-n + 0.5, 0.5) def _norm(value: ArrayLike) -> NDArray: value = np.atleast_1d(value) dtype = value.dtype if dtype.kind != "f": value = value.astype(np.float64) return value def _covinv(array): return np.linalg.inv(array) if array.ndim == 2 else 1.0 / array def _normalize_model_output(x, msg="Model should return numpy array"): if not isinstance(x, np.ndarray): warnings.warn( f"{msg}, but returns {type(x)}", PerformanceWarning, ) x = np.array(x) if x.dtype.kind != "f": return x.astype(float) return x def _shape_from_xe(xe): if isinstance(xe[0], Iterable): return tuple(len(xei) - 1 for xei in xe) return (len(xe) - 1,) def _model_parameters(model): # strip first argument from model ann = describe(model, annotations=True) args = iter(ann) next(args) return {k: ann[k] for k in args} _deprecated_content = { "BarlowBeestonLite": ("Template", Template), "barlow_beeston_lite_chi2_jsc": ("template_chi2_jsc", template_chi2_jsc), "barlow_beeston_lite_chi2_hpd": ("template_chi2_da", template_chi2_da), } def __getattr__(name: str) -> Any: if name in _deprecated_content: new_name, obj = _deprecated_content[name] warnings.warn( f"{name} was renamed to {new_name}, please import {new_name} instead", np.VisibleDeprecationWarning, stacklevel=2, ) return obj raise AttributeError iminuit-2.24.0/src/iminuit/experimental.py0000644000000000000000000000212214332717401015516 0ustar00""" Warning: As the name indicates, everything in this module is experimental. The API is not final, code here may be removed or altered in breaking ways without warning. Use at your own risk. """ from .util import merge_signatures def expanded(*callables): """ Return expanded callables with unified signatures. Uses :func:`merge_signatures` to unify the signatures and generates function wrappers that accept the merged signature and call the original function in the correct way. This is best explained by an example:: def f(x, y, z): ... def g(x, p): ... f2, g2 = expand_functions(f, g) # f2 is the equivalent of: def f2(x, y, z, p): return f(x, y, z) # g2 is the equivalent of: def g2(x, y, z, p): return g(x, p) """ varnames, mapping = merge_signatures(callables) total = ",".join(varnames) args = [",".join(varnames[i] for i in map) for map in mapping] lambdas = ",".join( f"lambda {total} : funcs[{i}]({arg})" for (i, arg) in enumerate(args) ) return eval(lambdas, {"funcs": callables}) iminuit-2.24.0/src/iminuit/minimize.py0000644000000000000000000001162514332717401014652 0ustar00"""Scipy interface for Minuit.""" from .minuit import Minuit import warnings import numpy as np def minimize( fun, x0, args=(), method="migrad", jac=None, hess=None, hessp=None, bounds=None, constraints=None, tol=None, callback=None, options=None, ): """ Interface to MIGRAD using the ``scipy.optimize.minimize`` API. This function provides the same interface as ``scipy.optimize.minimize``. If you are familiar with the latter, this allows you to use Minuit with a quick start. Eventually, you still may want to learn the interface of the :class:`Minuit` class, as it provides more functionality if you are interested in parameter uncertainties. For a general description of the arguments of this function, see ``scipy.optimize.minimize``. We list only a few in the following. Parameters ---------- method: str Allowed values are "migrad" or "simplex". Default: "migrad". options: dict Can be used to pass special settings to Minuit. All are optional. The following options are supported. *disp* (bool): Set to true to print convergence messages. Default: False. *stra* (int): Minuit strategy (0: fast/inaccurate, 1: balanced, 2: slow/accurate). Default: 1. *maxfun* (int): Maximum allowed number of iterations. Default: None. *maxfev* (int): Deprecated alias for *maxfun*. *eps* (sequence): Initial step size to numerical compute derivative. Minuit automatically refines this in subsequent iterations and is very insensitive to the initial choice. Default: 1. Returns ------- OptimizeResult Dict with attribute access that holds the result. It contains the following lists of fields. *x* (ndarray): Solution of optimization. *fun* (float): Value of objective function at minimum. *message* (str): Description of cause of termination. *hess_inv* (ndarray): Inverse of Hesse matrix at minimum (may not be exact). *nfev* (int): Number of function evaluations. *njev* (int): Number of jacobian evaluations. *minuit* (Minuit): Minuit object internally used to do the minimization. Use this to extract more information about the parameter errors. """ from scipy.optimize import OptimizeResult, Bounds x0 = np.atleast_1d(x0) if constraints is not None: raise ValueError("Constraints are not supported by Minuit, only bounds") if hess or hessp: warnings.warn("hess and hessp arguments cannot be handled and are ignored") def wrapped(func, args, callback=None): if callback is None: return lambda x: func(x, *args) def f(x): callback(x) return func(x, *args) return f wrapped_fun = wrapped(fun, args, callback) wrapped_fun.errordef = 0.5 # so that hesse is really second derivative if bool(jac): if jac is True: raise ValueError("jac=True is not supported, only jac=callable") assert hasattr(jac, "__call__") wrapped_grad = wrapped(jac, args) else: wrapped_grad = None m = Minuit(wrapped_fun, x0, grad=wrapped_grad) if bounds is not None: if isinstance(bounds, Bounds): m.limits = [(a, b) for a, b in zip(bounds.lb, bounds.ub)] else: m.limits = bounds if tol: m.tol = tol ncall = 0 if options: m.print_level = 2 if options.get("disp", False) else 0 if "maxiter" in options: warnings.warn("maxiter not supported, acts like maxfun instead") if "maxfev" in options: warnings.warn( "maxfev is deprecated, use maxfun instead", DeprecationWarning ) ncall = options.get("maxfun", options.get("maxfev", options.get("maxiter", 0))) errors = options.get("eps", None) if errors is not None: m.errors = errors m.strategy = options.get("stra", 1) if method == "migrad": m.migrad(ncall=ncall) elif method == "simplex": m.simplex(ncall=ncall) else: raise ValueError(f"keyword method={method} not understood") if m.valid: message = "Optimization terminated successfully" if m.accurate: message += "." else: message += ", but uncertainties are unrealiable." else: message = "Optimization failed." fmin = m.fmin if fmin.has_reached_call_limit: message += " Call limit was reached." if fmin.is_above_max_edm: message += " Estimated distance to minimum too large." n = len(x0) return OptimizeResult( x=np.array(m.values), success=m.valid, fun=m.fval, hess_inv=m.covariance if m.covariance is not None else np.ones((n, n)), message=message, nfev=m.nfcn, njev=m.ngrad, minuit=m, ) iminuit-2.24.0/src/iminuit/minuit.py0000644000000000000000000030204214332717401014332 0ustar00"""Minuit class.""" import warnings from iminuit import util as mutil from iminuit._core import ( FCN, MnContours, MnHesse, MnMachinePrecision, MnMigrad, MnMinos, MnPrint, MnSimplex, MnStrategy, MnUserParameterState, FunctionMinimum, ) import numpy as np from typing import ( Union, Sequence, Optional, Callable, Tuple, List, Dict, Iterable, Any, Collection, Set, Sized, ) from iminuit.typing import UserBound from iminuit._optional_dependencies import optional_module_for # Better use numpy.typing.ArrayLike in the future, but this # requires dropping Python-3.6 support _ArrayLike = Sequence MnPrint.global_level = 0 __all__ = ["Minuit"] class Minuit: """Function minimizer and error computer.""" __slots__ = ( "_fcn", "_strategy", "_tolerance", "_precision", "_values", "_errors", "_merrors", "_fixed", "_limits", "_fmin", "_covariance", "_var2pos", "_pos2var", "_init_state", "_last_state", ) _fmin: Optional[mutil.FMin] _covariance: Optional[mutil.Matrix] # Set errordef to this for a least-squares cost function. LEAST_SQUARES = 1.0 # Set errordef to this for a negated log-likelihood function. LIKELIHOOD = 0.5 @property def fcn(self) -> FCN: """Get cost function (usually a least-squares or likelihood function).""" return self._fcn @property def grad(self) -> Callable[[np.ndarray], np.ndarray]: """Get gradient function of the cost function.""" return self._fcn.gradient # type:ignore @property def pos2var(self) -> Tuple[str, ...]: """Map variable index to name.""" return self._pos2var @property def var2pos(self) -> Dict[str, int]: """Map variable name to index.""" return self._var2pos @property def parameters(self) -> Tuple[str, ...]: """ Get tuple of parameter names. This is an alias for :attr:`pos2var`. """ return self._pos2var @property def errordef(self) -> float: """ Access FCN increment above minimum that corresponds to one standard deviation. Default value is 1.0. `errordef` should be 1.0 for a least-squares cost function and 0.5 for a negative log-likelihood function. See section 1.5.1 on page 6 of the :download:`MINUIT2 User's Guide `. This parameter is also called *UP* in MINUIT documents. To make user code more readable, we provided two named constants:: m_lsq = Minuit(a_least_squares_function) m_lsq.errordef = Minuit.LEAST_SQUARES # == 1 m_nll = Minuit(a_likelihood_function) m_nll.errordef = Minuit.LIKELIHOOD # == 0.5 """ return self._fcn._errordef # type: ignore @errordef.setter def errordef(self, value: float) -> None: if value <= 0: raise ValueError(f"errordef={value} must be a positive number") self._fcn._errordef = value if self._fmin: self._fmin._src.errordef = value @property def precision(self) -> Optional[float]: """ Access estimated precision of the cost function. Default: None. If set to None, Minuit assumes the cost function is computed in double precision. If the precision of the cost function is lower (because it computes in single precision, for example) set this to some multiple of the smallest relative change of a parameter that still changes the function. """ return self._precision @precision.setter def precision(self, value: Optional[float]) -> None: if value is not None and not (value > 0): raise ValueError("precision must be a positive number or None") self._precision = value @property def tol(self) -> float: """ Access tolerance for convergence with the EDM criterion. Minuit detects converge with the EDM criterion. EDM stands for *Estimated Distance to Minimum*, it is mathematically described in the `MINUIT paper`_. The EDM criterion is well suited for statistical cost functions, since it stops the minimization when parameter improvements become small compared to parameter uncertainties. The convergence is detected when `edm < edm_max`, where `edm_max` is calculated as * Migrad: edm_max = 0.002 * tol * errordef * Simplex: edm_max = tol * errordef Users can set `tol` (default: 0.1) to a different value to either speed up convergence at the cost of a larger error on the fitted parameters and possibly invalid estimates for parameter uncertainties or smaller values to get more accurate parameter values, although this should never be necessary as the default is fine. If the tolerance is set to a very small value or zero, Minuit will use an internal lower limit for the tolerance. To restore the default use, one can assign `None`. Under some circumstances, Migrad is allowed to violate edm_max by a factor of 10. Users should not try to detect convergence by comparing edm with edm_max, but query :attr:`iminuit.util.FMin.is_above_max_edm`. """ return self._tolerance @tol.setter def tol(self, value: Optional[float]) -> None: if value is None: # used to reset tolerance value = 0.1 elif value < 0: raise ValueError("tolerance must be non-negative") self._tolerance = value @property def strategy(self) -> MnStrategy: """ Access current minimization strategy. You can assign an integer: - 0: Fast. Does not check a user-provided gradient. Does not improve Hesse matrix at minimum. Extra call to :meth:`hesse` after :meth:`migrad` is always needed for good error estimates. If you pass a user-provided gradient to MINUIT, convergence is **faster**. - 1: Default. Checks user-provided gradient against numerical gradient. Checks and usually improves Hesse matrix at minimum. Extra call to :meth:`hesse` after :meth:`migrad` is usually superfluous. If you pass a user-provided gradient to MINUIT, convergence is **slower**. - 2: Careful. Like 1, but does extra checks of intermediate Hessian matrix during minimization. The effect in benchmarks is a somewhat improved accuracy at the cost of more function evaluations. A similar effect can be achieved by reducing the tolerance :attr:`tol` for convergence at any strategy level. """ return self._strategy @strategy.setter def strategy(self, value: int) -> None: self._strategy.strategy = value @property def print_level(self) -> int: """ Access current print level. You can assign an integer: - 0: quiet (default) - 1: print minimal debug messages to terminal - 2: print more debug messages to terminal - 3: print even more debug messages to terminal Warnings -------- Setting print_level has the unwanted side-effect of setting the level globally for all Minuit instances in the current Python session. """ return MnPrint.global_level # type: ignore @print_level.setter def print_level(self, level: int) -> None: MnPrint.global_level = level @property def throw_nan(self) -> bool: """ Access whether to raise runtime error if the function evaluates to NaN. If you set this to True, an error is raised whenever the function evaluates to NaN. """ return self._fcn._throw_nan # type: ignore @throw_nan.setter def throw_nan(self, value: bool) -> None: self._fcn._throw_nan = value @property def values(self) -> mutil.ValueView: """ Access parameter values via an array-like view. Use to read or write current parameter values based on the parameter index or the parameter name as a string. If you change a parameter value and run :meth:`migrad`, the minimization will start from that value, similar for :meth:`hesse` and :meth:`minos`. See Also -------- errors, fixed, limits """ return self._values @values.setter def values(self, args: Iterable) -> None: self._values[:] = args @property def errors(self) -> mutil.ErrorView: """ Access parameter parabolic errors via an array-like view. Like :attr:`values`, but instead of reading or writing the values, you read or write the errors (which double as step sizes for MINUITs numerical gradient estimation). Only positive values are accepted when assigning to errors. See Also -------- values, fixed, limits """ return self._errors @errors.setter def errors(self, args: Iterable) -> None: self._errors[:] = args @property def fixed(self) -> mutil.FixedView: """ Access whether parameters are fixed via an array-like view. Use to read or write the fixation state of a parameter based on the parameter index or the parameter name as a string. If you change the state and run :meth:`migrad`, :meth:`hesse`, or :meth:`minos`, the new state is used. In case of complex fits, it can help to fix some parameters first and only minimize the function with respect to the other parameters, then release the fixed parameters and minimize again starting from that state. See Also -------- values, errors, limits """ return self._fixed @fixed.setter def fixed(self, args: Iterable) -> None: self._fixed[:] = args @property def limits(self) -> mutil.LimitView: """ Access parameter limits via a array-like view. Use to read or write the limits of a parameter based on the parameter index or the parameter name as a string. If you change the limits and run :meth:`migrad`, :meth:`hesse`, or :meth:`minos`, the new state is used. In case of complex fits, it can help to limit some parameters first, run Migrad, then remove the limits and run Migrad again. Limits will bias the result only if the best fit value is outside the limits, not if it is inside. Limits will affect the estimated Hesse uncertainties if the parameter is close to a limit. They do not affect the Minos uncertainties, because those are invariant to transformations and limits are implemented via a variable transformation. See Also -------- values, errors, fixed """ return self._limits @limits.setter def limits(self, args: Iterable) -> None: self._limits[:] = args @property def merrors(self) -> mutil.MErrors: """ Return a dict-like with Minos data objects. The Minos data objects contain the full status information of the Minos run. See Also -------- util.MError util.MErrors """ return self._merrors @property def covariance(self) -> Optional[mutil.Matrix]: r""" Return covariance matrix. The square-root of the diagonal elements of the covariance matrix correspond to a standard deviation for each parameter with 68 % coverage probability in the asymptotic limit (large samples). To get k standard deviations, multiply the covariance matrix with k^2. The submatrix formed by two parameters describes an ellipse. The asymptotic coverage probabilty of the standard ellipse is lower than 68 %. It can be computed from the :math:`\chi^2` distribution with 2 degrees of freedom. In general, to obtain a (hyper-)ellipsoid with coverage probability CL, one has to multiply the submatrix of the corresponding k parameters with a factor. For k = 1,2,3 and CL = 0.99 :: from scipy.stats import chi2 chi2(1).ppf(0.99) # 6.63... chi2(2).ppf(0.99) # 9.21... chi2(3).ppf(0.99) # 11.3... See Also -------- util.Matrix """ return self._covariance @property def npar(self) -> int: """Get number of parameters.""" return len(self._last_state) @property def nfit(self) -> int: """Get number of fitted parameters (fixed parameters not counted).""" return self.npar - sum(self.fixed) @property def ndof(self) -> int: """ Get number of degrees of freedom if cost function supports this. To support this feature, the cost function has to report the number of data points with a property called ``ndata``. Unbinned cost functions should return infinity. """ return self._fcn._ndata() - self.nfit # type: ignore @property def fmin(self) -> Optional[mutil.FMin]: """ Get function minimum data object. See Also -------- util.FMin """ return self._fmin @property def fval(self) -> Optional[float]: """ Get function value at minimum. This is an alias for :attr:`iminuit.util.FMin.fval`. See Also -------- util.FMin """ fm = self._fmin return fm.fval if fm else None @property def params(self) -> mutil.Params: """ Get list of current parameter data objects. See Also -------- init_params, util.Params """ return _get_params(self._last_state, self._merrors) @property def init_params(self) -> mutil.Params: """ Get list of current parameter data objects set to the initial fit state. See Also -------- params, util.Params """ return _get_params(self._init_state, mutil.MErrors()) @property def valid(self) -> bool: """ Return True if the function minimum is valid. This is an alias for :attr:`iminuit.util.FMin.is_valid`. See Also -------- util.FMin """ return self._fmin.is_valid if self._fmin else False @property def accurate(self) -> bool: """ Return True if the covariance matrix is accurate. This is an alias for :attr:`iminuit.util.FMin.has_accurate_covar`. See Also -------- util.FMin """ return self._fmin.has_accurate_covar if self._fmin else False @property def nfcn(self) -> int: """Get total number of function calls.""" return self._fcn._nfcn # type:ignore @property def ngrad(self) -> int: """Get total number of gradient calls.""" return self._fcn._ngrad # type:ignore def __init__( self, fcn: Callable, *args: Union[float, _ArrayLike[float]], grad: Callable = None, name: Collection[str] = None, **kwds: float, ): """ Initialize Minuit object. This does not start the minimization or perform any other work yet. Algorithms are started by calling the corresponding methods. Parameters ---------- fcn : Function to minimize. See notes for details on what kind of functions are accepted. *args : Starting values for the minimization as positional arguments. See notes for details on how to set starting values. grad : Function that calculates the gradient and returns an iterable object with one entry for each parameter, which is the derivative for that parameter. If None (default), Minuit will calculate the gradient numerically. name : If it is set, it overrides iminuit's function signature detection. **kwds : Starting values for the minimization as keyword arguments. See notes for details on how to set starting values. Notes ----- *Callables* By default, Minuit assumes that the callable `fcn` behaves like chi-square function, meaning that the function minimum in repeated identical random experiments is chi-square distributed up to an arbitrary additive constant. This is important for the correct error calculation. If `fcn` returns a log-likelihood, one should multiply the result with -2 to adapt it. If the function returns the negated log-likelihood, one can alternatively set the attribute `fcn.errordef` = :attr:`Minuit.LIKELIHOOD` or :attr:`Minuit.errordef` = :attr:`Minuit.LIKELIHOOD` after initialization to make Minuit calculate errors properly. Minuit reads the function signature of `fcn` to detect the number and names of the function parameters. Two kinds of function signatures are understood. a) Function with positional arguments. The function has positional arguments, one for each fit parameter. Example:: def fcn(a, b, c): ... The parameters a, b, c must accept a real number. iminuit automatically detects the parameters names in this case. More information about how the function signature is detected can be found in :func:`iminuit.util.describe`. b) Function with arguments passed as a single Numpy array. The function has a single argument which is a Numpy array. Example:: def fcn_np(x): ... To use this form, starting values need to be passed to Minuit in form as an array-like type, e.g. a numpy array, tuple or list. For more details, see "Parameter Keyword Arguments" further down. In some cases, the detection fails, for example, for a function like this:: def difficult_fcn(*args): ... To use such a function, set the `name` keyword as described further below. *Parameter initialization* Initial values for the minimization can be set with positional arguments or via keywords. This is best explained through an example:: def fcn(x, y): return (x - 2) ** 2 + (y - 3) ** 2 The following ways of passing starting values are equivalent:: Minuit(fcn, x=1, y=2) Minuit(fcn, y=2, x=1) # order is irrelevant when keywords are used ... Minuit(fcn, 1, 2) # ... but here the order matters Positional arguments can also be used if the function has no signature:: def fcn_no_sig(*args): # ... Minuit(fcn_no_sig, 1, 2) If the arguments are explicitly named with the `name` keyword described further below, keywords can be used for initialization:: Minuit(fcn_no_sig, x=1, y=2, name=("x", "y")) # this also works If the function accepts a single Numpy array, then the initial values must be passed as a single array-like object:: def fcn_np(x): return (x[0] - 2) ** 2 + (x[1] - 3) ** 2 Minuit(fcn_np, (1, 2)) Setting the values with keywords is not possible in this case. Minuit deduces the number of parameters from the length of the initialization sequence. See Also -------- migrad, hesse, minos, scan, simplex """ array_call = False if len(args) == 1 and isinstance(args[0], Iterable): array_call = True start = np.array(args[0]) else: start = np.array(args) del args annotated = mutil.describe(fcn, annotations=True) if name is None: name = list(annotated) if len(name) == 0 or (array_call and len(name) == 1): name = tuple(f"x{i}" for i in range(len(start))) elif len(name) == len(annotated): annotated = {new: annotated[old] for (old, new) in zip(annotated, name)} if len(start) == 0 and len(kwds) == 0: raise RuntimeError( "starting value(s) are required" + (f" for [{' '.join(name)}]" if name else "") ) # Maintain two dictionaries to easily convert between # parameter names and position self._pos2var = tuple(name) self._var2pos = {k: i for i, k in enumerate(name)} # set self.tol to default value self.tol = None # type:ignore self._strategy = MnStrategy(1) self._fcn = FCN( fcn, getattr(fcn, "grad", grad), array_call, getattr(fcn, "errordef", 1.0), ) self._init_state = _make_init_state(self._pos2var, start, kwds) self._values = mutil.ValueView(self) self._errors = mutil.ErrorView(self) self._fixed = mutil.FixedView(self) self._limits = mutil.LimitView(self) self.precision = getattr(fcn, "precision", None) self.reset() for k, lim in annotated.items(): if lim is not None: self.limits[k] = lim def fixto(self, key: mutil.Key, value: Union[float, Iterable[float]]) -> "Minuit": """ Fix parameter and set it to value. This is a convenience function to fix a parameter and set it to a value at the same time. It is equivalent to calling :attr:`fixed` and :attr:`values`. Parameters ---------- key : int, str, slice, list of int or str Key, which can be an index, name, slice, or list of indices or names. value : float or sequence of float Value(s) assigned to key(s). Returns ------- self """ index = mutil._key2index(self._var2pos, key) if isinstance(index, list): if mutil._ndim(value) == 0: # support basic broadcasting for i in index: self.fixto(i, value) else: assert isinstance(value, Iterable) assert isinstance(value, Sized) if len(value) != len(index): raise ValueError("length of argument does not match slice") for i, v in zip(index, value): self.fixto(i, v) else: self._last_state.fix(index) self._last_state.set_value(index, value) return self # return self for method chaining def reset(self) -> "Minuit": """ Reset minimization state to initial state. Leaves :attr:`strategy`, :attr:`precision`, :attr:`tol`, :attr:`errordef`, :attr:`print_level` unchanged. """ self._last_state = self._init_state self._fmin = None self._fcn._nfcn = 0 self._fcn._ngrad = 0 self._merrors = mutil.MErrors() self._covariance: mutil.Matrix = None return self # return self for method chaining and to autodisplay current state def migrad(self, ncall: int = None, iterate: int = 5) -> "Minuit": """ Run Migrad minimization. Migrad from the Minuit2 library is a robust minimisation algorithm which earned its reputation in 40+ years of almost exclusive usage in high-energy physics. How Migrad works is described in the `Minuit paper`_. It uses first and approximate second derivatives to achieve quadratic convergence near the minimum. Parameters ---------- ncall : Approximate maximum number of calls before minimization will be aborted. If set to None, use the adaptive heuristic from the Minuit2 library (Default: None). Note: The limit may be slightly violated, because the condition is checked only after a full iteration of the algorithm, which usually performs several function calls. iterate : Automatically call Migrad up to N times if convergence was not reached (Default: 5). This simple heuristic makes Migrad converge more often even if the numerical precision of the cost function is low. Setting this to 1 disables the feature. See Also -------- simplex, scan """ if ncall is None: ncall = 0 # tells C++ Minuit to use its internal heuristic if iterate < 1: raise ValueError("iterate must be at least 1") migrad = MnMigrad(self._fcn, self._last_state, self.strategy) t = mutil._Timer(self._fmin) with t: # Automatically call Migrad up to `iterate` times if minimum is not valid. # This simple heuristic makes Migrad converge more often. for _ in range(iterate): # workaround: precision must be set again after each call to MnMigrad if self._precision is not None: migrad.precision = self._precision fm = migrad(ncall, self._tolerance) if fm.is_valid or fm.has_reached_call_limit: break self._last_state = fm.state self._fmin = mutil.FMin( fm, "Migrad", self.nfcn, self.ngrad, self.ndof, self._edm_goal(migrad_factor=True), t.value, ) self._make_covariance() return self # return self for method chaining and to autodisplay current state def simplex(self, ncall: int = None) -> "Minuit": """ Run Simplex minimization. Simplex from the Minuit2 C++ library is a variant of the Nelder-Mead algorithm to find the minimum of a function. It does not make use of derivatives. `The Wikipedia has a good article on the Nelder-Mead method `_. Parameters ---------- ncall : Approximate maximum number of calls before minimization will be aborted. If set to None, use the adaptive heuristic from the Minuit2 library (Default: None). Note: The limit may be slightly violated, because the condition is checked only after a full iteration of the algorithm, which usually performs several function calls. Notes ----- The Simplex method usually converges more slowly than Migrad, but performs better in certain cases, the Rosenbrock function is a notable example. Unlike Migrad, the Simplex method does not have quadratic convergence near the minimum, so it is a good approach to run Migrad after Simplex to obtain an accurate solution in fewer steps. Simplex may also be useful to get close to the minimum from an unsuitable starting point. The convergence criterion for Simplex is also based on EDM, but the threshold is much more lax than that of Migrad (see :attr:`Minuit.tol` for details). This was made so that Simplex stops early when getting near the minimum, to give the user a chance to switch to the more efficient Migrad algorithm to finish the minimization. Early stopping can be avoided by setting Minuit.tol to an accordingly smaller value, however. """ if ncall is None: ncall = 0 # tells C++ Minuit to use its internal heuristic simplex = MnSimplex(self._fcn, self._last_state, self.strategy) if self._precision is not None: simplex.precision = self._precision t = mutil._Timer(self._fmin) with t: fm = simplex(ncall, self._tolerance) self._last_state = fm.state self._fmin = mutil.FMin( fm, "Simplex", self.nfcn, self.ngrad, self.ndof, self._edm_goal(), t.value, ) self._covariance = None self._merrors = mutil.MErrors() return self # return self for method chaining and to autodisplay current state def scan(self, ncall: int = None) -> "Minuit": """ Brute-force minimization. Scans the function on a regular hypercube grid, whose bounds are defined either by parameter limits if present or by Minuit.values +/- Minuit.errors. Minuit.errors are initialized to very small values by default, too small for this scan. They should be increased before running scan or limits should be set. The scan evaluates the function exactly at the limit boundary, so the function should be defined there. Parameters ---------- ncall : Approximate number of function calls to spend on the scan. The actual number will be close to this, the scan uses ncall^(1/npar) steps per cube dimension. If no value is given, a heuristic is used to set ncall. Notes ----- The scan can return an invalid minimum, this is not a cause for alarm. It just minimizes the cost function, the EDM value is only computed after the scan found a best point. If the best point still has a bad EDM value, the minimum is considered invalid. But even if it is considered valid, it is probably not accurate, since the tolerance is very lax. One should always run :meth:`migrad` after the scan. This implementation here does a full scan of the hypercube in Python. Originally, this was supposed to use MnScan from C++ Minuit2, but MnScan is unsuitable. It does a 1D scan with 41 steps (not configurable) for each parameter in sequence, so it is not actually scanning the full hypercube. It first scans one parameter, then starts the scan of the second parameter from the best value of the first and so on. This fails easily when the parameters are correlated. """ # Implementation notes: # Returning a valid FunctionMinimum object was a major challenge, because C++ # Minuit2 does not allow one to initialize data objects with data, it forces one # to go through algorithm objects. Because of that design, the Minuit2 C++ # interface forces one to compute the gradient and second derivatives for the # starting values, even though these are not used in a scan. We turn a # disadvantage into an advantage here by tricking Minuit2 into computing updates # of the step sizes and to estimate the EDM value. # Running MnScan would look like this: # scan = MnScan(self._fcn, self._last_state, self.strategy) # fm = scan(0, 0) # args are ignored # self._last_state = fm.state # self._fmin = mutil.FMin(fm, self._fcn.nfcn, self._fcn.ngrad, self._tolerance) n = self.nfit if ncall is None: ncall = self._migrad_maxcall() nstep = int(ncall ** (1 / n)) if self._last_state == self._init_state: # avoid overriding initial state self._last_state = MnUserParameterState(self._last_state) x = np.empty(self.npar + 1) x[self.npar] = np.inf lims = list(self.limits) for i, (low, up) in enumerate(lims): v = self.values[i] e = self.errors[i] if self.fixed[i]: lims[i] = v, v else: lims[i] = ( v - e if low == -np.inf else low, v + e if up == np.inf else up, ) def run(ipar: int) -> None: if ipar == self.npar: r = self.fcn(x[: self.npar]) if r < x[self.npar]: x[self.npar] = r self.values[:] = x[: self.npar] return low, up = lims[ipar] if low == up: x[ipar] = low run(ipar + 1) else: for xi in np.linspace(low, up, nstep): x[ipar] = xi run(ipar + 1) t = mutil._Timer(self._fmin) with t: run(0) edm_goal = self._edm_goal() fm = FunctionMinimum(self._fcn, self._last_state, self.strategy, edm_goal) self._last_state = fm.state self._fmin = mutil.FMin( fm, "Scan", self.nfcn, self.ngrad, self.ndof, edm_goal, t.value, ) self._covariance = None self._merrors = mutil.MErrors() return self # return self for method chaining and to autodisplay current state def scipy( self, method: Union[str, Callable] = None, ncall: int = None, hess: Any = None, hessp: Any = None, constraints: Iterable = None, ) -> "Minuit": """ Minimize with SciPy algorithms. Parameters ---------- method : str or Callable, optional Which scipy method to use. ncall : int, optional Function call limit. hess : Callable, optional Function that computes the Hessian matrix. It must use the exact same calling conversion as the original fcn (several arguments which are numbers or a single array argument). hessp : Callable, optional Function that computes the product of the Hessian matrix with a vector. It must use the same calling conversion as the original fcn (several arguments which are numbers or a single array argument) and end with another argument which is an arbitrary vector. constraints : scipy.optimize.LinearConstraint or scipy.optimize.NonlinearConstraint, optional Linear or non-linear constraints, see docs of :func:`scipy.optimize.minimize` look for the `constraints` parameter. The function used in the constraint must use the exact same calling convention as the original fcn, see hess parameter for details. No parameters may be omitted in the signature, even if those parameters are not used in the constraint. Notes ----- The call limit may be violated since many algorithms checks the call limit only after a full iteraction of their algorithm, which consists of several function calls. Some algorithms do not check the number of function calls at all, in this case the call limit acts on the number of iterations of the algorithm. This issue should be fixed in scipy. The SciPy minimizers use their own internal rule for convergence. The EDM criterion is evaluated only after the original algorithm already stopped. This means that usually SciPy minimizers will use more iterations than Migrad and the tolerance :attr:`tol` has no effect on SciPy minimizers. """ try: from scipy.optimize import ( minimize, Bounds, NonlinearConstraint, LinearConstraint, approx_fprime, ) except ModuleNotFoundError as exc: exc.msg += "\n\nPlease install scipy to use scipy minimizers in iminuit." raise if ncall is None: ncall = self._migrad_maxcall() cfree = ~np.array(self.fixed[:], dtype=bool) cpar = np.array(self.values[:]) no_fixed_parameters = np.all(cfree) if no_fixed_parameters: class Wrapped: __slots__ = ("fcn",) def __init__(self, fcn): self.fcn = fcn if self.fcn._array_call: def __call__(self, par): return self.fcn(par) else: def __call__(self, par): return self.fcn(*par) WrappedGrad = Wrapped WrappedHess = Wrapped class WrappedHessp: __slots__ = ("fcn",) def __init__(self, fcn): self.fcn = fcn if self.fcn._array_call: def __call__(self, par, v): return self.fcn(par, v) else: def __call__(self, par, v): return self.fcn(*par, v) else: class Wrapped: # type:ignore __slots__ = ("fcn", "free", "par") def __init__(self, fcn): self.fcn = fcn self.free = cfree self.par = cpar if self.fcn._array_call: def __call__(self, par): self.par[self.free] = par return self.fcn(self.par) else: def __call__(self, par): self.par[self.free] = par return self.fcn(*self.par) class WrappedGrad(Wrapped): # type:ignore def __call__(self, par): g = super().__call__(par) return np.atleast_1d(g)[self.free] class WrappedHess(Wrapped): # type:ignore def __init__(self, fcn): super().__init__(fcn) self.freem = np.outer(self.free, self.free) n = np.sum(self.free) self.shape = n, n def __call__(self, par): h = super().__call__(par) return np.atleast_2d(h)[self.freem].reshape(self.shape) class WrappedHessp: # type:ignore __slots__ = ("fcn", "free", "par", "vec") def __init__(self, fcn): self.fcn = fcn self.free = cfree self.par = cpar self.vec = np.zeros_like(self.par) if self.fcn._array_call: def __call__(self, par, v): self.par[self.free] = par self.vec[self.free] = v return self.fcn(self.par, self.vec)[self.free] else: def __call__(self, par, v): self.par[self.free] = par self.vec[self.free] = v return self.fcn(*self.par, self.vec)[self.free] fcn = Wrapped(self.fcn._fcn) grad = self.fcn._grad grad = WrappedGrad(grad) if grad else None if hess: hess = WrappedHess(hess) if hessp: hessp = WrappedHessp(hessp) if constraints is not None: if isinstance(constraints, dict): raise ValueError("setting constraints with dicts is not supported") if not isinstance(constraints, Iterable): constraints = [constraints] else: constraints = list(constraints) for i, c in enumerate(constraints): if isinstance(c, NonlinearConstraint): c.fun = Wrapped(c.fun) elif isinstance(c, LinearConstraint): if not no_fixed_parameters: x = cpar.copy() x[cfree] = 0 shift = np.dot(c.A, x) lb = c.lb - shift ub = c.ub - shift A = np.atleast_2d(c.A)[:, cfree] constraints[i] = LinearConstraint(A, lb, ub, c.keep_feasible) else: raise ValueError( "setting constraints with dicts is not supported, use " "LinearConstraint or NonlinearConstraint from scipy.optimize." ) pr = self._mnprecision() # Limits for scipy need to be a little bit tighter than the ones for Minuit # so that the Jacobian of the transformation is not zero or infinite. start = [] lower_bound = [] upper_bound = [] has_limits = False for p in self.params: if p.is_fixed: continue has_limits |= p.has_limits # ensure lower < x < upper for Minuit ai = -np.inf if p.lower_limit is None else p.lower_limit bi = np.inf if p.upper_limit is None else p.upper_limit if ai > 0: ai *= 1 + pr.eps2 elif ai < 0: ai *= 1 - pr.eps2 else: ai = pr.eps2 if bi > 0: bi *= 1 - pr.eps2 elif bi < 0: bi *= 1 + pr.eps2 else: bi = -pr.eps2 xi = np.clip(p.value, ai, bi) lower_bound.append(ai) upper_bound.append(bi) start.append(xi) if method is None: # like in scipy.optimize.minimize if constraints: method = "SLSQP" elif has_limits: method = "L-BFGS-B" else: method = "BFGS" # various workarounds for API inconsistencies in scipy.optimize.minimize options = {"maxiter": ncall} if method in ( "Nelder-Mead", "Powell", ): options["maxfev"] = ncall del options["maxiter"] if method in ("L-BFGS-B", "TNC"): options["maxfun"] = ncall del options["maxiter"] if method in ("COBYLA", "SLSQP", "trust-constr") and constraints is None: constraints = () t = mutil._Timer(self._fmin) with t: r = minimize( fcn, start, method=method, bounds=Bounds(lower_bound, upper_bound, keep_feasible=True) if has_limits else None, jac=grad, hess=hess, hessp=hessp, constraints=constraints, options=options, ) if self.print_level > 0: print(r) self.fcn._nfcn += r["nfev"] if grad: self.fcn._ngrad += r.get("njev", 0) # Get inverse Hesse matrix, working around many inconsistencies in scipy. # Try in order: # 1) If hess_inv is returned as full matrix as result, use that. # 2) If hess is returned as full matrix, invert it and use that. # - These two are approximations to the exact Hessian. - # 3) If externally computed hessian was passed to method, use that. # Hessian is considered accurate then. matrix = None needs_invert = False if "hess_inv" in r: matrix = r.hess_inv elif "hess" in r: matrix = r.hess needs_invert = True # hess_inv is a function, need to convert to full matrix if hasattr(matrix, "__call__"): assert matrix is not None # for mypy matrix = matrix(np.eye(self.nfit)) accurate_covar = bool(hess) or bool(hessp) # Newton-CG neither returns hessian nor inverted hessian if matrix is None: if accurate_covar: if hessp: matrix = [hessp(r.x, ei) for ei in np.eye(self.nfit)] else: matrix = hess(r.x) needs_invert = True if needs_invert: matrix = np.linalg.inv(matrix) # type:ignore # Last resort: use parameter step sizes as "errors" if matrix is None: matrix = np.zeros((self.nfit, self.nfit)) i = 0 for p in self.params: if p.is_fixed: continue matrix[i, i] = p.error**2 i += 1 if "grad" in r: # trust-constr has "grad" and "jac", but "grad" is "jac"! jac = r.grad elif "jac" in r: jac = r.jac else: dx = np.sqrt(np.diag(matrix) * 1e-10) jac = approx_fprime(r.x, fcn, epsilon=dx) edm_goal = self._edm_goal(migrad_factor=True) fm = FunctionMinimum( self._last_state.trafo, r.x, matrix, jac, r.fun, self.errordef, edm_goal, self.nfcn, ncall, accurate_covar, ) self._last_state = fm.state self._fmin = mutil.FMin( fm, f"SciPy[{method}]", self.nfcn, self.ngrad, self.ndof, edm_goal, t.value, ) if accurate_covar: self._make_covariance() else: if self.strategy.strategy > 0: self.hesse() return self def visualize(self, plot: Callable = None, **kwargs): """ Visualize agreement of current model with data (requires matplotlib). This generates a plot of the data/model agreement, using the current parameter values, if the likelihood function supports this, otherwise AttributeError is raised. Parameters ---------- plot : Callable, optional This function tries to call the visualize method on the cost function, which accepts the current model parameters as an array-like and potentially further keyword arguments, and draws a visualization into the current matplotlib axes. If the cost function does not provide a visualize method or if you want to override it, pass the function here. kwargs : Other keyword arguments are forwarded to the plot function. """ return self._visualize(plot)(self.values, **kwargs) def hesse(self, ncall: int = None) -> "Minuit": """ Run Hesse algorithm to compute asymptotic errors. The Hesse method estimates the covariance matrix by inverting the matrix of `second derivatives (Hesse matrix) at the minimum `_. To get parameters correlations, you need to use this. The Minos algorithm is another way to estimate parameter uncertainties, see :meth:`minos`. Parameters ---------- ncall : Approximate upper limit for the number of calls made by the Hesse algorithm. If set to None, use the adaptive heuristic from the Minuit2 library (Default: None). Notes ----- The covariance matrix is asymptotically (in large samples) valid. By valid we mean that confidence intervals constructed from the errors contain the true value with a well-known coverage probability (68 % for each interval). In finite samples, this is likely to be true if your cost function looks like a hyperparabola around the minimum. In practice, the errors very likely have correct coverage if the results from Minos and Hesse methods agree. It is possible to construct artifical functions where this rule is violated, but in practice it should always work. See Also -------- minos """ ncall = 0 if ncall is None else int(ncall) # Should be fixed upstream: workaround for segfault in MnHesse when all # parameters are fixed if self.nfit == 0: warnings.warn( "Hesse called with all parameters fixed", mutil.IMinuitWarning, stacklevel=2, ) return self if self._fmin_does_not_exist_or_last_state_was_modified(): # create a seed minimum edm_goal = self._edm_goal(migrad_factor=True) fm = FunctionMinimum( self._fcn, self._last_state, self._strategy, edm_goal, ) self._fmin = mutil.FMin( fm, "External", self.nfcn, self.ngrad, self.ndof, edm_goal, 0 ) self._merrors = mutil.MErrors() assert self._fmin is not None fm = self._fmin._src # update _fmin with Hesse hesse = MnHesse(self.strategy) t = mutil._Timer(self._fmin) with t: hesse(self._fcn, fm, ncall, self._fmin.edm_goal) self._last_state = fm.state self._fmin = mutil.FMin( fm, self._fmin.algorithm, self.nfcn, self.ngrad, self.ndof, self._fmin.edm_goal, t.value, ) self._make_covariance() return self # return self for method chaining and to autodisplay current state def minos( self, *parameters: Union[int, str], cl: float = None, ncall: int = None, ) -> "Minuit": """ Run Minos algorithm to compute confidence intervals. The Minos algorithm uses the profile likelihood method to compute (generally asymmetric) confidence intervals. It scans the negative log-likelihood or (equivalently) the least-squares cost function around the minimum to construct a confidence interval. Parameters ---------- *parameters : Names of parameters to generate Minos errors for. If no positional arguments are given, Minos is run for each parameter. cl : float or None, optional Confidence level of the interval. If not set or None, a standard 68 % interval is computed (default). If 0 < cl < 1, the value is interpreted as the confidence level (a probability). For convenience, values cl >= 1 are interpreted as the probability content of a central symmetric interval covering that many standard deviations of a normal distribution. For example, cl=1 is interpreted as 68.3 %, and cl=2 is 84.3 %, and so on. Using values other than 0.68, 0.9, 0.95, 0.99, 1, 2, 3, 4, 5 require the scipy module. ncall : int or None, optional Limit the number of calls made by Minos. If None, an adaptive internal heuristic of the Minuit2 library is used (Default: None). Notes ----- Asymptotically (large samples), the Minos interval has a coverage probability equal to the given confidence level. The coverage probability is the probability for the interval to contain the true value in repeated identical experiments. The interval is invariant to transformations and thus not distorted by parameter limits, unless the limits intersect with the confidence interval. As a rule-of-thumb: when the confidence intervals computed with the Hesse and Minos algorithms differ strongly, the Minos intervals are preferred. Otherwise, Hesse intervals are preferred. Running Minos is computationally expensive when there are many fit parameters. Effectively, it scans over one parameter in small steps and runs a full minimisation for all other parameters of the cost function for each scan point. This requires many more function evaluations than running the Hesse algorithm. """ ncall = 0 if ncall is None else int(ncall) factor = _cl_to_errordef(cl, 1, 1.0) if self._fmin_does_not_exist_or_last_state_was_modified(): self.hesse() # creates self._fmin assert self._fmin is not None fm = self._fmin._src if not self.valid: raise RuntimeError(f"Function minimum is not valid: {repr(self._fmin)}") if len(parameters) == 0: ipars = [ipar for ipar in range(self.npar) if not self.fixed[ipar]] else: ipars = [] for par in parameters: ip, pname = self._normalize_key(par) if self.fixed[ip]: warnings.warn( f"Cannot scan over fixed parameter {pname!r}", mutil.IMinuitWarning, ) else: ipars.append(ip) t = mutil._Timer(self._fmin) with t: with _TemporaryErrordef(self._fcn, factor): minos = MnMinos(self._fcn, fm, self.strategy) for ipar in ipars: par = self._pos2var[ipar] me = minos(ipar, ncall, self._tolerance) self._merrors[par] = mutil.MError( me.number, par, me.lower, me.upper, me.is_valid, me.lower_valid, me.upper_valid, me.at_lower_limit, me.at_upper_limit, me.at_lower_max_fcn, me.at_upper_max_fcn, me.lower_new_min, me.upper_new_min, me.nfcn, me.min, ) if self._fmin: self._fmin._nfcn = self.nfcn self._fmin._ngrad = self.ngrad self._fmin._time = t.value return self # return self for method chaining and to autodisplay current state def mnprofile( self, vname: Union[int, str], *, size: int = 30, bound: Union[float, UserBound] = 2, grid: _ArrayLike[float] = None, subtract_min: bool = False, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: r""" Get Minos profile over a specified interval. Scans over one parameter and minimises the function with respect to all other parameters for each scan point. Parameters ---------- vname : int or str Parameter to scan over. size : int, optional Number of scanning points (Default: 100). Ignored if grid is set. bound : tuple of float or float, optional If bound is tuple, (left, right) scanning bound. If bound is a number, it specifies an interval of N :math:`\sigma` symmetrically around the minimum (Default: 2). Ignored if grid is set. grid : array-like, optional Parameter values on which to compute the profile. If grid is set, size and bound are ignored. subtract_min : bool, optional If true, subtract offset so that smallest value is zero (Default: False). Returns ------- array of float Parameter values where the profile was computed. array of float Profile values. array of bool Whether minimisation in each point succeeded or not. """ ipar, pname = self._normalize_key(vname) del vname if grid is not None: x = np.array(grid, dtype=float) if x.ndim != 1: raise ValueError("grid must be 1D array-like") else: a, b = self._normalize_bound(pname, bound) x = np.linspace(a, b, size, dtype=float) y = np.empty_like(x) status = np.empty(len(x), dtype=bool) state = MnUserParameterState(self._last_state) # copy state.fix(ipar) for i, v in enumerate(x): state.set_value(ipar, v) migrad = MnMigrad(self._fcn, state, self.strategy) fm = migrad(0, self._tolerance) if not fm.is_valid: warnings.warn( f"MIGRAD fails to converge for {pname}={v}", mutil.IMinuitWarning ) status[i] = fm.is_valid y[i] = fm.fval if subtract_min: y -= np.min(y) return x, y, status def draw_mnprofile( self, vname: Union[int, str], *, band: bool = True, text: bool = True, **kwargs ) -> Tuple[np.ndarray, np.ndarray]: r""" Draw Minos profile over a specified interval (requires matplotlib). See :meth:`mnprofile` for details and shared arguments. The following additional arguments are accepted. Parameters ---------- band : bool, optional If true, show a band to indicate the Hesse error interval (Default: True). text : bool, optional If true, show text a title with the function value and the Hesse error (Default: True). Examples -------- .. plot:: plots/mnprofile.py :include-source: See Also -------- mnprofile, profile, draw_profile """ ipar, pname = self._normalize_key(vname) del vname if "subtract_min" not in kwargs: kwargs["subtract_min"] = True x, y, _ = self.mnprofile(ipar, **kwargs) return self._draw_profile(ipar, x, y, band, text) def profile( self, vname: Union[int, str], *, size: int = 100, bound: Union[float, UserBound] = 2, grid: _ArrayLike[float] = None, subtract_min: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: r""" Calculate 1D cost function profile over a range. A 1D scan of the cost function around the minimum, useful to inspect the minimum. For a fit with several free parameters this is not the same as the Minos profile computed by :meth:`mncontour`. Parameters ---------- vname : int or str Parameter to scan over. size : int, optional Number of scanning points (Default: 100). Ignored if grid is set. bound : tuple of float or float, optional If bound is tuple, (left, right) scanning bound. If bound is a number, it specifies an interval of N :math:`\sigma` symmetrically around the minimum (Default: 2). Ignored if grid is set. grid : array-like, optional Parameter values on which to compute the profile. If grid is set, size and bound are ignored. subtract_min : bool, optional If true, subtract offset so that smallest value is zero (Default: False). Returns ------- array of float Parameter values. array of float Function values. See Also -------- mnprofile """ ipar, par = self._normalize_key(vname) del vname if grid is not None: x = np.array(grid, dtype=float) if x.ndim != 1: raise ValueError("grid must be 1D array-like") else: a, b = self._normalize_bound(par, bound) x = np.linspace(a, b, size, dtype=float) y = np.empty_like(x) values = np.array(self.values) for i, vi in enumerate(x): values[ipar] = vi y[i] = self.fcn(values) if subtract_min: y -= np.min(y) return x, y def draw_profile( self, vname: Union[int, str], *, band: bool = True, text: bool = True, **kwargs ) -> Tuple[np.ndarray, np.ndarray]: """ Draw 1D cost function profile over a range (requires matplotlib). See :meth:`profile` for details and shared arguments. The following additional arguments are accepted. Parameters ---------- band : bool, optional If true, show a band to indicate the Hesse error interval (Default: True). text : bool, optional If true, show text a title with the function value and the Hesse error (Default: True). See Also -------- profile, mnprofile, draw_mnprofile """ ipar, pname = self._normalize_key(vname) del vname if "subtract_min" not in kwargs: kwargs["subtract_min"] = True x, y = self.profile(ipar, **kwargs) return self._draw_profile(ipar, x, y, band, text) def _draw_profile( self, ipar: int, x: np.ndarray, y: np.ndarray, band: bool, text: bool, ) -> Tuple[np.ndarray, np.ndarray]: from matplotlib import pyplot as plt pname = self._pos2var[ipar] plt.plot(x, y) plt.xlabel(pname) plt.ylabel("FCN") v = self.values[ipar] plt.axvline(v, color="k", linestyle="--") vmin = None vmax = None if pname in self.merrors: vmin = v + self.merrors[pname].lower vmax = v + self.merrors[pname].upper else: vmin = v - self.errors[ipar] vmax = v + self.errors[ipar] if vmin is not None and band: plt.axvspan(vmin, vmax, facecolor="0.8") if text: plt.title( (f"{pname} = {v:.3g}") if vmin is None else ( "{} = {:.3g} - {:.3g} + {:.3g}".format(pname, v, v - vmin, vmax - v) ), fontsize="large", ) return x, y def contour( self, x: Union[int, str], y: Union[int, str], *, size: int = 50, bound: Union[float, Iterable[Tuple[float, float]]] = 2, grid: Tuple[_ArrayLike, _ArrayLike] = None, subtract_min: bool = False, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: r""" Get a 2D contour of the function around the minimum. It computes the contour via a function scan over two parameters, while keeping all other parameters fixed. The related :meth:`mncontour` works differently: for each pair of parameter values in the scan, it minimises the function with the respect to all other parameters. This method is useful to inspect the function near the minimum to detect issues (the contours should look smooth). It is not a confidence region unless the function only has two parameters. Use :meth:`mncontour` to compute confidence regions. Parameters ---------- x : int or str First parameter for scan. y : int or str Second parameter for scan. size : int or tuple of int, optional Number of scanning points per parameter (Default: 50). A tuple is interpreted as the number of scanning points per parameter. Ignored if grid is set. bound : float or tuple of floats, optional If bound is 2x2 array, [[v1min,v1max],[v2min,v2max]]. If bound is a number, it specifies how many :math:`\sigma` symmetrically from minimum (minimum+- bound*:math:`\sigma`). (Default: 2). Ignored if grid is set. grid : tuple of array-like, optional Grid points to scan over. If grid is set, size and bound are ignored. subtract_min : Subtract minimum from return values (Default: False). Returns ------- array of float Parameter values of first parameter. array of float Parameter values of second parameter. 2D array of float Function values. See Also -------- mncontour, mnprofile """ ix, xname = self._normalize_key(x) iy, yname = self._normalize_key(y) del x del y if grid is not None: xg, yg = grid xv = np.array(xg, dtype=float) yv = np.array(yg, dtype=float) if xv.ndim != 1 or yv.ndim != 1: raise ValueError("grid per parameter must be 1D array-like") else: if isinstance(bound, Iterable): xb, yb = bound xrange = self._normalize_bound(xname, xb) yrange = self._normalize_bound(yname, yb) else: n = float(bound) xrange = self._normalize_bound(xname, n) yrange = self._normalize_bound(yname, n) if isinstance(size, Iterable): xsize, ysize = size else: xsize = size ysize = size xv = np.linspace(xrange[0], xrange[1], xsize) yv = np.linspace(yrange[0], yrange[1], ysize) zv = np.empty((len(xv), len(yv)), dtype=float) values = np.array(self.values) for i, xi in enumerate(xv): values[ix] = xi for j, yi in enumerate(yv): values[iy] = yi zv[i, j] = self._fcn(values) if subtract_min: zv -= np.min(zv) return xv, yv, zv def draw_contour( self, x: Union[int, str], y: Union[int, str], **kwargs, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ Draw 2D contour around minimum (requires matplotlib). See :meth:`contour` for details on parameters and interpretation. Please also read the docs of :meth:`mncontour` to understand the difference between the two. See Also -------- contour, mncontour, draw_mncontour """ from matplotlib import pyplot as plt ix, xname = self._normalize_key(x) iy, yname = self._normalize_key(y) del x del y if "subtract_min" not in kwargs: kwargs["subtract_min"] = True vx, vy, vz = self.contour(ix, iy, **kwargs) v = [self.errordef * (i + 1) for i in range(4)] CS = plt.contour(vx, vy, vz.T, v) plt.clabel(CS, v) plt.xlabel(xname) plt.ylabel(yname) plt.axhline(self.values[iy], color="k", ls="--") plt.axvline(self.values[ix], color="k", ls="--") return vx, vy, vz def mncontour( self, x: Union[int, str], y: Union[int, str], *, cl: float = None, size: int = 100, interpolated: int = 0, experimental: bool = False, ) -> np.ndarray: """ Get 2D Minos confidence region. This scans over two parameters and minimises all other free parameters for each scan point. This scan produces a statistical confidence region according to the `profile likelihood method `_ with a confidence level `cl`, which is asymptotically equal to the coverage probability of the confidence region according to `Wilks' theorem `. Note that 1D projections of the 2D confidence region are larger than 1D Minos intervals computed for the same confidence level. This is not an error, but a consequence of Wilks' theorem. The calculation is expensive since a numerical minimisation has to be performed at various points. Parameters ---------- x : str Variable name of the first parameter. y : str Variable name of the second parameter. cl : float or None, optional Confidence level of the contour. If not set or None, a standard 68 % contour is computed (default). If 0 < cl < 1, the value is interpreted as the confidence level (a probability). For convenience, values cl >= 1 are interpreted as the probability content of a central symmetric interval covering that many standard deviations of a normal distribution. For example, cl=1 is interpreted as 68.3 %, and cl=2 is 84.3 %, and so on. Using values other than 0.68, 0.9, 0.95, 0.99, 1, 2, 3, 4, 5 require the scipy module. size : int, optional Number of points on the contour to find (default: 100). Increasing this makes the contour smoother, but requires more computation time. interpolated : int, optional Number of interpolated points on the contour (default: 0). If you set this to a value larger than size, cubic spline interpolation is used to generate a smoother curve and the interpolated coordinates are returned. Values smaller than size are ignored. Good results can be obtained with size=20, interpolated=200. This requires scipy. experimental : bool, optional If true, use experimental implementation to compute contour, otherwise use MnContour from the Minuit2 library. The experimental implementation was found to succeed in cases where MnContour produced no reasonable result, but is slower and not yet well tested in practice. Use with caution and report back any issues via Github. Returns ------- array of float (N x 2) Contour points of the form [[x1, y1]...[xn, yn]]. Note that N = size + 1, the last point [xn, yn] is identical to [x1, y1]. This makes it easier to draw a closed contour. See Also -------- contour, mnprofile """ ix, xname = self._normalize_key(x) iy, yname = self._normalize_key(y) del x del y factor = _cl_to_errordef(cl, 2, 0.68) if self._fmin_does_not_exist_or_last_state_was_modified(): self.hesse() # creates self._fmin if not self.valid: raise RuntimeError(f"Function minimum is not valid: {repr(self._fmin)}") pars = {xname, yname} - self._free_parameters() if pars: raise ValueError( f"mncontour can only be run on free parameters, not on {pars}" ) if experimental: ce = self._experimental_mncontour(factor, ix, iy, size) else: with _TemporaryErrordef(self._fcn, factor): assert self._fmin is not None mnc = MnContours(self._fcn, self._fmin._src, self.strategy) ce = mnc(ix, iy, size)[2] pts = np.asarray(ce) # add starting point at end to close the contour pts = np.append(pts, pts[:1], axis=0) if interpolated > size: with optional_module_for("interpolation"): from scipy.interpolate import CubicSpline xg = np.linspace(0, 1, len(pts)) spl = CubicSpline(xg, pts, bc_type="periodic") pts = spl(np.linspace(0, 1, interpolated)) return pts def draw_mncontour( self, x: Union[int, str], y: Union[int, str], *, cl: Union[float, _ArrayLike[float]] = None, size: int = 100, interpolated: int = 0, experimental: bool = False, ) -> Any: """ Draw 2D Minos confidence region (requires matplotlib). See :meth:`mncontour` for details on the interpretation of the region and for the parameters accepted by this function. Examples -------- .. plot:: plots/mncontour.py :include-source: Returns ------- ContourSet Instance of a ContourSet class from matplot.contour. See Also -------- mncontour """ from matplotlib import __version__ as mpl_version from matplotlib import pyplot as plt from matplotlib.path import Path from matplotlib.contour import ContourSet ix, xname = self._normalize_key(x) iy, yname = self._normalize_key(y) mpl_version = tuple(map(int, mpl_version.split("."))) cls = [mutil._replace_none(x, 0.68) for x in mutil._iterate(cl)] c_val = [] c_pts = [] codes = [] for cl in cls: pts = self.mncontour( ix, iy, cl=cl, size=size, interpolated=interpolated, experimental=experimental, ) n_lineto = len(pts) - 2 if mpl_version < (3, 5): n_lineto -= 1 # pragma: no cover c_val.append(cl) c_pts.append([pts]) # level can have more than one contour in mpl codes.append([[Path.MOVETO] + [Path.LINETO] * n_lineto + [Path.CLOSEPOLY]]) assert len(c_val) == len(codes), f"{len(c_val)} {len(codes)}" cs = ContourSet(plt.gca(), c_val, c_pts, codes) plt.clabel(cs) plt.xlabel(xname) plt.ylabel(yname) return cs def draw_mnmatrix( self, *, cl: Union[float, _ArrayLike[float]] = None, size: int = 100, experimental: bool = False, figsize=None, ) -> Any: """ Draw matrix of Minos scans (requires matplotlib). This draws a matrix of Minos likelihood scans, meaning that the likelihood is minimized with respect to the parameters that are not scanned over. The diagonal cells of the matrix show the 1D scan, the off-diagonal cells show 2D scans for all unique pairs of parameters. The projected edges of the 2D contours do not align with the 1D intervals, because of Wilks' theorem. The levels for 2D confidence regions are higher. For more information on the interpretation of 2D confidence regions, see :meth:`mncontour`. Parameters ---------- cl : float or array-like of floats, optional See :meth:`mncontour`. size : int, optional See :meth:`mncontour` experimental : bool, optional See :meth:`mncontour` figsize : (float, float) or None, optional Width and height of figure in inches. Examples -------- .. plot:: plots/mnmatrix.py :include-source: Returns ------- fig, ax Figure and axes instances generated by matplotlib. See Also -------- mncontour """ if not self.valid: raise RuntimeError(f"Function minimum is not valid: {repr(self._fmin)}") pars = [p for p in self.parameters if not self.fixed[p]] npar = len(pars) if npar == 0: raise RuntimeError("all parameters are fixed") cls = [mutil._replace_none(x, 0.68) for x in mutil._iterate(cl)] if len(cls) == 0: raise ValueError("cl must have at least one value") from matplotlib import pyplot as plt fig, ax = plt.subplots( npar, npar, figsize=figsize, constrained_layout=True, squeeze=False, ) prange = {p: (np.inf, -np.inf) for p in pars} with mutil.ProgressBar( max_value=npar + (npar * (npar + 1) // 2 - npar) * len(cls) ) as bar: for i, par1 in enumerate(pars): plt.sca(ax[i, i]) fmax = 0 for k, cl in enumerate(cls): f = _cl_to_errordef(cl, 1, 0.68) fmax = max(fmax, f) plt.axhline(f, color=f"C{k}") bound = fmax**0.5 + 1 for iter in range(5): x, y, ok = self.mnprofile(par1, bound=bound, subtract_min=True) x = x[ok] y = y[ok] if y[0] > fmax and y[-1] > fmax: break bound *= 1.6 bar += 1 plt.plot(x, y, "k") a, b = prange[par1] extremes = [] for k, (xk, yk) in enumerate(zip(x, y)): if yk < fmax and y[k - 1] > fmax: extremes.append(x[k - 1]) if yk > fmax and y[k - 1] < fmax: extremes.append(xk) a = min(*extremes, a) b = max(*extremes, b) prange[par1] = (a, b) plt.ylim(0, fmax + 0.5) for j in range(i): par2 = pars[j] plt.sca(ax[i, j]) plt.plot(self.values[par2], self.values[par1], "+", color="k") for k, cli in enumerate(cls): pts = self.mncontour( par1, par2, cl=cli, size=size, experimental=experimental ) bar += 1 if len(pts) > 0: x, y = np.transpose(pts) plt.plot(y, x, color=f"C{k}") for r, p in ((x, par1), (y, par2)): a, b = prange[p] a = min(np.min(r), a) b = max(np.max(r), b) prange[p] = (a, b) ax[j, i].set_visible(False) for i, par1 in enumerate(pars): ax[i, i].set_xlim(*prange[par1]) if i > 0: ax[i, 0].set_ylabel(par1) ax[-1, i].set_xlabel(par1) for j in range(i): par2 = pars[j] ax[j, i].set_xlim(*prange[par1]) ax[j, i].set_ylim(*prange[par2]) return fig, ax def interactive( self, plot: Callable = None, raise_on_exception=False, **kwargs, ): """ Return fitting widget (requires ipywidgets, IPython, matplotlib). A fitting widget is returned which can be displayed and manipulated in a Jupyter notebook to find good starting parameters and to debug the fit. Parameters ---------- plot : Callable, optional To visualize the fit, interactive tries to access the visualize method on the cost function, which accepts the current model parameters as an array-like and potentially further keyword arguments, and draws a visualization into the current matplotlib axes. If the cost function does not provide a visualize method or if you want to override it, pass the function here. raise_on_exception : bool, optional The default is to catch exceptions in the plot function and convert them into a plotted message. In unit tests, raise_on_exception should be set to True to allow detecting errors. **kwargs : Any other keyword arguments are forwarded to the plot function. Examples -------- .. plot:: plots/interactive.py :include-source: """ with warnings.catch_warnings(): # ipywidgets produces deprecation warnings through use of internal APIs :( warnings.simplefilter("ignore") try: from ipywidgets import ( HBox, VBox, Output, FloatSlider, Button, ToggleButton, Layout, Dropdown, ) from ipywidgets.widgets.interaction import show_inline_matplotlib_plots from IPython.display import clear_output from matplotlib import pyplot as plt except ModuleNotFoundError as e: e.msg += ( "\n\nPlease install ipywidgets, IPython, and matplotlib to " "enable interactive" ) raise plot = self._visualize(plot) def plot_with_frame(args, from_fit, report_success): trans = plt.gca().transAxes try: with warnings.catch_warnings(): if self.fcn._array_call: plot([args], **kwargs) # prevent unpacking of array else: plot(args, **kwargs) except Exception: if raise_on_exception: raise import traceback plt.figtext( 0.01, 0.5, traceback.format_exc(), ha="left", va="center", transform=trans, color="r", ) return if from_fit: fval = self.fmin.fval else: fval = self.fcn(args) plt.text( 0.05, 1.05, f"FCN = {fval:.3f}", transform=trans, fontsize="x-large", ) if from_fit and report_success: plt.text( 0.95, 1.05, f"{'success' if self.valid and self.accurate else 'FAILURE'}", transform=trans, fontsize="x-large", ha="right", ) class ParameterBox(HBox): def __init__(self, par, val, min, max, step, fix): self.par = par self.slider = FloatSlider( val, min=a, max=b, step=step, description=par, continuous_update=True, layout=Layout(min_width="70%"), ) self.fix = ToggleButton( fix, description="Fix", layout=Layout(width="3.1em") ) self.opt = ToggleButton( False, description="Opt", layout=Layout(width="3.5em") ) self.opt.observe(self.on_opt_toggled, "value") super().__init__([self.slider, self.fix, self.opt]) def on_opt_toggled(self, change): self.slider.disabled = self.opt.value on_slider_change(None) def fit(): if algo_choice.value == "Migrad": self.migrad() elif algo_choice.value == "Scipy": self.scipy() elif algo_choice.value == "Simplex": self.simplex() return False else: assert False # pragma: no cover, should never happen return True def on_slider_change(change): if out.block: return args = [x.slider.value for x in parameters] from_fit = False report_success = False if any(x.opt.value for x in parameters): save = self.fixed[:] self.fixed = [not x.opt.value for x in parameters] self.values = args report_success = fit() args = self.values[:] out.block = True for x, val in zip(parameters, args): x.slider.value = val out.block = False self.fixed = save from_fit = True with out: clear_output(wait=True) plot_with_frame(args, from_fit, report_success) with warnings.catch_warnings(): warnings.simplefilter("ignore") show_inline_matplotlib_plots() def on_fit_button_clicked(change): for x in parameters: self.values[x.par] = x.slider.value self.fixed[x.par] = x.fix.value report_success = fit() out.block = True for x in parameters: val = self.values[x.par] if val < x.slider.min: x.slider.min = val elif val > x.slider.max: x.slider.max = val x.slider.value = val out.block = False with out: clear_output(wait=True) plot_with_frame(self.values, True, report_success) show_inline_matplotlib_plots() def on_update_button_clicked(change): for x in parameters: x.slider.continuous_update = not x.slider.continuous_update def on_reset_button_clicked(change): self.reset() out.block = True for x in parameters: x.slider.value = self.values[x.par] out.block = False on_slider_change(None) parameters = [] for par in self.parameters: val = self.values[par] step = mutil._guess_initial_step(val) a, b = self.limits[par] # safety margin to avoid overflow warnings a = a + 1e-300 if np.isfinite(a) else val - 100 * step b = b - 1e-300 if np.isfinite(b) else val + 100 * step parameters.append(ParameterBox(par, val, a, b, step, self.fixed[par])) fit_button = Button(description="Fit") fit_button.on_click(on_fit_button_clicked) update_button = ToggleButton(True, description="Continuous") update_button.observe(on_update_button_clicked) reset_button = Button(description="Reset") reset_button.on_click(on_reset_button_clicked) algo_choice = Dropdown( options=["Migrad", "Scipy", "Simplex"], value="Migrad" ) ui = VBox( [ HBox([fit_button, update_button, reset_button, algo_choice]), VBox(parameters), ] ) out = Output() out.block = False for x in parameters: x.slider.observe(on_slider_change, "value") show_inline_matplotlib_plots() on_slider_change(None) return HBox([out, ui]) def _free_parameters(self) -> Set[str]: return set(mp.name for mp in self._last_state if not mp.is_fixed) def _mnprecision(self) -> MnMachinePrecision: pr = MnMachinePrecision() if self._precision is not None: pr.eps = self._precision return pr def _normalize_key(self, key: Union[int, str]) -> Tuple[int, str]: if isinstance(key, int): if key >= self.npar: raise ValueError(f"parameter {key} is out of range (max: {self.npar})") return key, self._pos2var[key] if key not in self._var2pos: raise ValueError(f"unknown parameter {key!r}") return self._var2pos[key], key def _normalize_bound( self, vname: str, bound: Union[float, UserBound, Tuple[float, float]] ) -> Tuple[float, float]: if isinstance(bound, Iterable): return mutil._normalize_limit(bound) if not self.accurate: warnings.warn( "Specified nsigma bound, but error matrix is not accurate", mutil.IMinuitWarning, ) start = self.values[vname] sigma = self.errors[vname] return (start - bound * sigma, start + bound * sigma) def _copy_state_if_needed(self): # If FunctionMinimum exists, _last_state may be a reference to its user state. # The state is read-only in C++, but mutable in Python. To not violate # invariants, we need to make a copy of the state when the user requests a # modification. If a copy was already made (_last_state is already a copy), # no further copy has to be made. # # If FunctionMinimum does not exist, we don't want to copy. We want to # implicitly modify _init_state; _last_state is an alias for _init_state, then. if self._fmin and self._last_state == self._fmin._src.state: self._last_state = MnUserParameterState(self._last_state) def _make_covariance(self) -> None: if self._last_state.has_covariance: cov = self._last_state.covariance m = mutil.Matrix(self._var2pos) n = len(m) if cov.nrow < self.npar: ext2int = {} k = 0 for mp in self._last_state: if not mp.is_fixed: ext2int[mp.number] = k k += 1 m.fill(0) for e, i in ext2int.items(): for f, j in ext2int.items(): m[e, f] = cov[i, j] else: n = len(m) for i in range(n): for j in range(n): m[i, j] = cov[i, j] self._covariance = m else: self._covariance = None def _edm_goal(self, migrad_factor=False) -> float: # EDM goal # - taken from the source code, see VariableMeticBuilder::Minimum and # ModularFunctionMinimizer::Minimize # - goal is used to detect convergence but violations by 10x are also accepted; # see VariableMetricBuilder.cxx:425 edm_goal = max( self.tol * self.errordef, self._mnprecision().eps2 # type:ignore ) if migrad_factor: edm_goal *= 2e-3 return edm_goal def _migrad_maxcall(self) -> int: n = self.nfit return 200 + 100 * n + 5 * n * n def _fmin_does_not_exist_or_last_state_was_modified(self) -> bool: return not self._fmin or self._fmin._src.state is not self._last_state def __repr__(self): """Get detailed text representation.""" s = [] if self.fmin is not None: s.append(repr(self.fmin)) s.append(repr(self.params)) if self.merrors: s.append(repr(self.merrors)) if self.covariance is not None: s.append(repr(self.covariance)) return "\n".join(s) def __str__(self): """Get user-friendly text representation.""" s = [] if self.fmin is not None: s.append(str(self.fmin)) s.append(str(self.params)) if self.merrors: s.append(str(self.merrors)) if self.covariance is not None: s.append(str(self.covariance)) return "\n".join(s) def _repr_html_(self): s = "" if self.fmin is not None: s += self.fmin._repr_html_() s += self.params._repr_html_() if self.merrors: s += self.merrors._repr_html_() if self.covariance is not None: s += self.covariance._repr_html_() if self.fmin is not None: try: import matplotlib.pyplot as plt import io with _TemporaryFigure(5, 4): self.visualize() with io.StringIO() as io: plt.savefig(io, format="svg", dpi=10) io.seek(0) s += io.read() except (ModuleNotFoundError, AttributeError, ValueError): pass return s def _repr_pretty_(self, p, cycle): if cycle: p.text("") else: p.text(str(self)) def _visualize(self, plot): pyfcn = self.fcn._fcn if plot is None: if hasattr(pyfcn, "visualize"): plot = pyfcn.visualize else: raise AttributeError( f"class {pyfcn.__class__.__name__} has no visualize method, " "please use the 'plot' keyword to pass a visualization function" ) return plot def _experimental_mncontour( self, factor: float, ix: int, iy: int, size: int ) -> List[Tuple[float, float]]: from scipy.optimize import root_scalar center = self.values[[ix, iy]] assert self.covariance is not None t, u = np.linalg.eig( [ [self.covariance[ix, ix], self.covariance[ix, iy]], [self.covariance[ix, iy], self.covariance[iy, iy]], ] ) s = (t * factor) ** 0.5 ce = [] for phi in np.linspace(-np.pi, np.pi, size, endpoint=False): def args(z): r = u @ ( z * s[0] * np.cos(phi), z * s[1] * np.sin(phi), ) x = r[0] + center[0] lim = self.limits[ix] if lim is not None: x = max(lim[0], min(x, lim[1])) y = r[1] + center[1] if lim is not None: y = max(lim[0], min(y, lim[1])) return x, y def scan(z): state = MnUserParameterState(self._last_state) # copy state.fix(ix) state.fix(iy) xy = args(z) state.set_value(ix, xy[0]) state.set_value(iy, xy[1]) migrad = MnMigrad(self._fcn, state, max(0, self.strategy.strategy - 1)) fm = migrad(0, self._tolerance) return fm.fval - self.fval - factor * self._fcn._errordef # find bracket a = 0.5 while scan(a) > 0 and a > 1e-7: a *= 0.5 # pragma: no cover if a < 1e-7: ce.append((np.nan, np.nan)) # pragma: no cover continue # pragma: no cover b = 1.2 while scan(b) < 0 and b < 8: b *= 1.1 if b > 8: ce.append(args(b)) continue # low xtol was found to be sufficient in experimental trials r = root_scalar(scan, bracket=(a, b), xtol=1e-3) ce.append(args(r.root) if r.converged else (np.nan, np.nan)) return ce def _make_init_state( pos2var: Tuple[str, ...], args: np.ndarray, kwds: Dict[str, float] ) -> MnUserParameterState: nargs = len(args) # check kwds if nargs: if kwds: raise RuntimeError( f"positional arguments cannot be mixed with " f"parameter keyword arguments {kwds}" ) else: for kw in kwds: if kw not in pos2var: raise RuntimeError( f"{kw} is not one of the parameters [{' '.join(pos2var)}]" ) nargs = len(kwds) if len(pos2var) != nargs: raise RuntimeError( f"{nargs} values given for {len(pos2var)} function parameter(s)" ) state = MnUserParameterState() for i, x in enumerate(pos2var): val = kwds[x] if kwds else args[i] err = mutil._guess_initial_step(val) state.add(x, val, err) return state def _get_params(mps: MnUserParameterState, merrors: mutil.MErrors) -> mutil.Params: def get_me(name: str) -> Optional[Tuple[float, float]]: if name in merrors: me = merrors[name] return me.lower, me.upper return None return mutil.Params( ( mutil.Param( mp.number, mp.name, mp.value, mp.error, get_me(mp.name), mp.is_const, mp.is_fixed, mp.lower_limit if mp.has_lower_limit else None, mp.upper_limit if mp.has_upper_limit else None, ) for mp in mps ), ) class _TemporaryErrordef: def __init__(self, fcn: FCN, factor: float): self.saved = fcn._errordef self.fcn = fcn self.fcn._errordef *= factor def __enter__(self) -> None: pass def __exit__(self, *args: object) -> None: self.fcn._errordef = self.saved class _TemporaryFigure: def __init__(self, w, h): from matplotlib import pyplot as plt self.plt = plt self.fig = self.plt.figure(figsize=(w, h), constrained_layout=True) def __enter__(self) -> None: pass def __exit__(self, *args: object) -> None: self.plt.close(self.fig) def _cl_to_errordef(cl, npar, default): assert 0 < npar < 3 cl = float(default if cl is None else cl) if cl <= 0: raise ValueError("cl must be positive") if npar == 1: if cl >= 1.0: factor = cl**2 else: factor = { 0.68: 0.988946481478023, # chi2(1).ppf(0.68) 0.90: 2.705543454095404, # chi2(1).ppf(0.9) 0.95: 3.841458820694124, # chi2(1).ppf(0.95) 0.99: 6.634896601021215, # chi2(1).ppf(0.99) }.get(cl, 0.0) else: factor = { 0.68: 2.27886856637673, # chi2(2).ppf(0.68) 0.90: 4.605170185988092, # chi2(2).ppf(0.9) 0.95: 5.991464547107979, # chi2(2).ppf(0.95) 0.99: 9.21034037197618, # chi2(2).ppf(0.99) 1.0: 2.295748928898636, # chi2(2).ppf(chi2(1).cdf(1)) 2.0: 6.180074306244168, # chi2(2).ppf(chi2(1).cdf(2 ** 2)) 3.0: 11.829158081900795, # chi2(2).ppf(chi2(1).cdf(3 ** 2)) 4.0: 19.333908611934685, # chi2(2).ppf(chi2(1).cdf(4 ** 2)) 5.0: 28.743702426935496, # chi2(2).ppf(chi2(1).cdf(5 ** 2)) }.get(cl, 0.0) if factor == 0.0: try: from scipy.stats import chi2 except ModuleNotFoundError as exc: exc.msg += ( "\n\n" "You set an uncommon cl value, " "scipy is needed to process it. Please install scipy." ) raise if cl >= 1.0: cl = chi2(1).cdf(cl**2) # convert sigmas into confidence level factor = chi2(npar).ppf(cl) # convert confidence level to errordef return factor iminuit-2.24.0/src/iminuit/pdg_format.py0000644000000000000000000002422514332717401015153 0ustar00# Copyright 2020 Hans Dembinski # Redistribution and use in source and binary forms, with or without modification, are # permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this list of # conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, this list # of conditions and the following disclaimer in the documentation and/or other materials # provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. """ PDG formatting of numbers with uncertainties. The PDG uses a special rounding rule for quantities with uncertainties. The description quoted from M. Tanabashi et al. (Particle Data Group), Phys. Rev. D 98, 030001 (2018), https://doi.org/10.1103/PhysRevD.98.030001: "The basic rule states that if the three highest order digits of the error lie between 100 and 354, we round to two significant digits. If they lie between 355 and 949, we round to one significant digit. Finally, if they lie between 950 and 999, we round up to 1000 and keep two significant digits. In all cases, the central value is given with a precision that matches that of the error. So, for example, the result (coming from an average) 0.827 +- 0.119 would appear as 0.83 +- 0.12, while 0.827 +- 0.367 would turn into 0.8 +- 0.4." In addition, the LHCb Editorial Board declared that in case of several errors, the most precise one defines the number of digits shown. This module offers functions that convert values and errors into a string representation. Documentation follows these guidelines: https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard """ import math from typing import List term = (" %s", " +%s", " ± %s", " (%s)", "(%s)E%+03i", True, None) latex = ( " {}_{%s}", "^{+%s}", r" \pm %s", r" (\mathrm{%s})", r"(%s) \times 10^{%i}", True, (r"\mathrm{NaN}", r"\infty"), ) def pdg_format(value, error, *errors, labels=None, format=term, leader=None, exp=None): r""" Return formatted value with uncertainties according to PDG rules. Examples -------- >>> pdg_format(2.3456, 0.123, 0.0123) '2.346 ± 0.123 ± 0.012' >>> pdg_format(2.3456, 0.123, 0.0123, leader=0) '2.35 ± 0.12 ± 0.01' >>> pdg_format(1.2, (0.1, 0.2), 0.3, format=latex) '1.2 {}_{-0.1}^{+0.2} \\pm 0.3' >>> pdg_format(2.3e-09, 0.354e-09, format=latex) '(2.30 \\pm 0.35) \\times 10^{-9}' >>> pdg_format(2.3e3, 0.1e3, 0.2e3, format=latex, labels=('stat', 'sys')) '(2.3 \\pm 0.1 (\\mathrm{stat}) \\pm 0.2 (\\mathrm{sys})) \\times 10^{3}' >>> pdg_format(1.234, -0.11, 0.22, 0.3, format=latex) '1.23 {}_{-0.11}^{+0.22} \\pm 0.30' Parameters ---------- value : float Estimated value. error : float or tuple of floats Uncertainty of value. A positive number is interpreted as a symmetric uncertainty (value +- error). Asymmetric uncertainties are passed as a tuple of positive numbers (x - xmin, xmax - x) OR as a negative number xmin - x immediately followed by a positive number xmax - x. *errors Optional further uncertainties. labels : sequence of str, optional Optional labels for the different uncertainties (e.g. 'sys', 'stat', 'lumi'). A label that starts with one of the characters '_\^<' is used verbatim, otherwise it is wrapped according to the format spec for labels. format : tuple, optional Formatting specification. Structure: ( , , , , , , ) leader : int, optional Index of uncertainty that should be used to determine the number of digits shown, if there are several uncertainties. Default is to use the smallest uncertainty. If asymmetric uncertainties are passed as tuples or subsequent pairs of negative and positive numbers, the index is that of the pair. exp : int, optional Exponent to use for scientific notation. If omitted, a heuristic algorithm selects a suitable exponent. Returns ------- str Formatted string. """ fmt_ne, fmt_pe, fmt_se, fmt_lab, fmt_sc, strip, trans = format strings, nexp = _round([value, error, *errors], leader, exp) if strip: strings = _strip(strings) if trans: for i, x in enumerate(strings): c = x[-1] if c == "n": # nan, -nan x = trans[0] elif c == "f": # inf, -inf if x[0] == "-": x = "-" + trans[1] x = trans[1] strings[i] = x s = strings[0] asym = False liter = iter(labels) if labels is not None else None for si in strings[1:]: if si[0] == "-": asym = True s += fmt_ne % si elif asym: asym = False s += fmt_pe % si else: s += fmt_se % si if liter and not asym: y = next(liter) if y[0] in r"_\^<": s += y else: s += fmt_lab % y if nexp == 0: return s else: return fmt_sc % (s, nexp) def _strip(items: List[str]) -> List[str]: assert all("e" not in x for x in items) # ignore inf and nan mask = tuple(i for (i, s) in enumerate(items) if "." in s) if mask: # strip common trailing "0"s first = items[mask[0]] for i in range(len(first)): if all(items[k][-1 - i] == "0" for k in mask): i += 1 else: break # maybe strip common trailing "." after stripping "0"s if i > 0 and all(items[k][-1 - i] == "." for k in mask): i += 1 if i > 0: for k in mask: items[k] = items[k][:-i] # turn negative zero into positive zero if items[0] == "-0": items[0] = "0" return items def _find_smallest_nonzero_abs_value(seq): k = None xmin = float("infinity") for i, x in enumerate(seq): x = abs(x) if x > 0 and x < xmin: xmin = x k = i return k, xmin def _is_asym(value): if hasattr(value, "__len__") and hasattr(value, "__getitem__"): if len(value) != 2: raise ValueError("sequence must have two elements") return True return False def _unpack(values): assert len(values) > 0 assert not _is_asym(values[0]) result = [values[0]] for v in values[1:]: if _is_asym(v): result.append(-abs(v[0])) result.append(v[1]) else: result.append(v) return result def _unpacked_index(values, index): k = 0 for i in range(index): k += 2 if values[k] < 0 else 1 return k def _round(values, leader, n_exp_extern): assert len(values) >= 1 assert not _is_asym(values[0]) values = _unpack(values) if leader is None: # select leading error that determines precision leader, lerror = _find_smallest_nonzero_abs_value(values[1:]) if leader is not None: leader += 1 else: leader = _unpacked_index(values[1:], leader) + 1 asym = math.copysign(1, values[leader]) < 0 # also works for NaN and -0.0 if asym: offset, lerror = _find_smallest_nonzero_abs_value( values[leader : leader + 2] ) if offset is None: leader = None else: leader += offset else: lerror = abs(values[leader]) def fmt(x, n_digits): return ("%%.%if" % max(n_digits, 0)) % x n_exp = None if math.isfinite(lerror) and lerror > 0: n_exp = int(math.floor(math.log10(lerror))) + 1 if n_exp is None: leader = None if math.isfinite(values[0]) and values[0] != 0: n_exp = int(math.floor(math.log10(abs(values[0])))) else: # n_exp cannot be determined return [str(x) for x in values], 0 if leader is None: # invalid leading error, cannot determine digits scale = 10**-n_exp return ([fmt(v * scale, 4) for v in values], n_exp) scale = 10**-n_exp digits = round(lerror * scale, 3) if digits < 0.355: n_digits = 2 digits = round(digits, 2) else: n_digits = 1 if digits < 0.95: digits = round(digits, 1) else: digits = 1.0 if n_exp_extern is None: if abs(n_exp) > 2: n_exp_extern = int(round(n_exp / 3) * 3) else: n_exp_extern = 0 if math.isfinite(values[0]) and values[0] != 0: n = math.floor(math.log10(abs(values[0])) / 3) * 3 if n > n_exp_extern: n_exp_extern = n shift = n_exp - n_exp_extern values = [ fmt( ( round(x * scale, n_digits) if i != leader else math.copysign(digits, values[leader]) ) * 10**shift, n_digits - shift, ) for (i, x) in enumerate(values) ] return values, n_exp - shift iminuit-2.24.0/src/iminuit/testing.py0000644000000000000000000000273314332717401014506 0ustar00""" Common test functions for optimizers. Also see: https://en.wikipedia.org/wiki/Test_functions_for_optimization """ def rosenbrock(x, y): """ Rosenbrock function. Minimum: f(1, 1) = 0. https://en.wikipedia.org/wiki/Rosenbrock_function """ return (1 - x) ** 2 + 100 * (y - x**2) ** 2 def rosenbrock_grad(x, y): """Gradient of Rosenbrock function.""" return (-400 * x * (-(x**2) + y) + 2 * x - 2, -200 * x**2 + 200 * y) def ackley(x, y): """ Ackley function. Minimum: f(0, 0) = 0. https://en.wikipedia.org/wiki/Ackley_function """ from math import sqrt, exp, cos, pi, e term1 = -20 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) term2 = -exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) return term1 + term2 + 20 + e def beale(x, y): """ Beale function. Minimum: f(3, 0.5) = 0. https://en.wikipedia.org/wiki/Test_functions_for_optimization """ term1 = 1.5 - x + x * y term2 = 2.25 - x + x * y**2 term3 = 2.625 - x + x * y**3 return term1 * term1 + term2 * term2 + term3 * term3 def matyas(x, y): """ Matyas function. Minimum: f(0, 0) = 0. https://en.wikipedia.org/wiki/Test_functions_for_optimization """ return 0.26 * (x**2 + y**2) - 0.48 * x * y def sphere_np(x): """ Sphere function for variable number of arguments. Minimum: f(0, ..., 0) = 0. https://en.wikipedia.org/wiki/Test_functions_for_optimization """ import numpy as np return np.sum(x**2) iminuit-2.24.0/src/iminuit/typing.py0000644000000000000000000000333214332717401014337 0ustar00""" Types for iminuit. These are used by mypy and similar tools. """ from typing import Protocol, Optional, List, Union, runtime_checkable, NamedTuple from numpy.typing import NDArray import numpy as np import dataclasses import sys if sys.version_info < (3, 9): from typing_extensions import Annotated # noqa pragma: no cover else: from typing import Annotated # noqa pragma: no cover # Key for ValueView, ErrorView, etc. Key = Union[int, str, slice, List[Union[int, str]]] @runtime_checkable class Model(Protocol): """Type for user-defined model.""" def __call__(self, x: np.ndarray, *args: float) -> np.ndarray: """Evaluate model at locations x and return results as an array.""" ... # pragma: no cover @runtime_checkable class LossFunction(Protocol): """Type for user-defined loss function for LeastSquares clas.""" def __call__(self, z: NDArray) -> NDArray: """Evaluate loss function on values.""" ... # pragma: no cover class UserBound(NamedTuple): """Type for user-defined limit.""" min: Optional[float] max: Optional[float] @dataclasses.dataclass class Gt: """Annotation compatible with annotated-types.""" gt: float @dataclasses.dataclass class Ge: """Annotation compatible with annotated-types.""" ge: float @dataclasses.dataclass class Lt: """Annotation compatible with annotated-types.""" lt: float @dataclasses.dataclass class Le: """Annotation compatible with annotated-types.""" le: float @dataclasses.dataclass class Interval: """Annotation compatible with annotated-types.""" gt: Optional[float] = None ge: Optional[float] = None lt: Optional[float] = None le: Optional[float] = None iminuit-2.24.0/src/iminuit/util.py0000644000000000000000000014102214332717401014001 0ustar00""" Data classes and utilities used by :class:`iminuit.Minuit`. You can look up the interface of data classes that iminuit uses here. """ from __future__ import annotations import inspect from collections import OrderedDict from argparse import Namespace from iminuit import _repr_html, _repr_text, _deprecated from iminuit.typing import Key, UserBound from iminuit.warnings import IMinuitWarning, HesseFailedWarning, PerformanceWarning import numpy as np from numpy.typing import NDArray from typing import ( overload, Any, List, Union, Dict, Iterable, Generator, Tuple, Optional, Callable, Collection, ) import abc from time import monotonic import warnings import sys if sys.version_info < (3, 9): from typing_extensions import Annotated, get_args, get_origin # pragma: no cover else: from typing import Annotated, get_args, get_origin # pragma: no cover __all__ = ( "IMinuitWarning", "HesseFailedWarning", "PerformanceWarning", "ValueView", "FixedView", "LimitView", "Matrix", "FMin", "Param", "Params", "MError", "MErrors", "make_func_code", "make_with_signature", "merge_signatures", "describe", ) class BasicView(abc.ABC): """ Array-like view of parameter state. Derived classes need to implement methods _set and _get to access specific properties of the parameter state. :meta private: """ __slots__ = ("_minuit", "_ndim") def __init__(self, minuit: Any, ndim: int = 0): # Users should not call this __init__, instances are created by the library self._minuit = minuit self._ndim = ndim def __iter__(self) -> Generator: """Get iterator over values.""" for i in range(len(self)): yield self._get(i) def __len__(self) -> int: """Get number of paramters.""" return self._minuit.npar # type: ignore @abc.abstractmethod def _get(self, idx: int) -> Any: return NotImplemented # pragma: no cover @abc.abstractmethod def _set(self, idx: int, value: Any) -> None: NotImplemented # pragma: no cover def __getitem__(self, key: Key) -> Any: """ Get values of the view. Parameters ---------- key: int, str, slice, list of int or str If the key is an int or str, return corresponding value. If it is a slice, list of int or str, return the corresponding subset. """ index = _key2index(self._minuit._var2pos, key) if isinstance(index, list): return [self._get(i) for i in index] return self._get(index) def __setitem__(self, key: Key, value: Any) -> None: """Assign new value at key, which can be index, parameter name, or slice.""" self._minuit._copy_state_if_needed() index = _key2index(self._minuit._var2pos, key) if isinstance(index, list): if _ndim(value) == self._ndim: # support basic broadcasting for i in index: self._set(i, value) else: if len(value) != len(index): raise ValueError("length of argument does not match slice") for i, v in zip(index, value): self._set(i, v) else: self._set(index, value) def __eq__(self, other: object) -> bool: """Return true if all values are equal.""" a, b = np.broadcast_arrays(self, other) # type:ignore return bool(np.all(a == b)) def __repr__(self) -> str: """Get detailed text representation.""" s = f"<{self.__class__.__name__}" for k, v in zip(self._minuit._pos2var, self): s += f" {k}={v}" s += ">" return s def to_dict(self) -> Dict[str, float]: """Obtain dict representation.""" return {k: self._get(i) for i, k in enumerate(self._minuit._pos2var)} def _ndim(obj: Any) -> int: nd = 0 while isinstance(obj, Iterable): nd += 1 for x in obj: if x is not None: obj = x break else: break return nd class ValueView(BasicView): """Array-like view of parameter values.""" def _get(self, i: int) -> float: return self._minuit._last_state[i].value # type:ignore def _set(self, i: int, value: float) -> None: self._minuit._last_state.set_value(i, value) class ErrorView(BasicView): """Array-like view of parameter errors.""" def _get(self, i: int) -> float: return self._minuit._last_state[i].error # type:ignore def _set(self, i: int, value: float) -> None: if value <= 0: warnings.warn( "Assigned errors must be positive. " "Non-positive values are replaced by a heuristic.", IMinuitWarning, ) value = _guess_initial_step(value) self._minuit._last_state.set_error(i, value) class FixedView(BasicView): """Array-like view of whether parameters are fixed.""" def _get(self, i: int) -> bool: return self._minuit._last_state[i].is_fixed # type:ignore def _set(self, i: int, fix: bool) -> None: if fix: self._minuit._last_state.fix(i) else: self._minuit._last_state.release(i) def __invert__(self) -> list: """Return list with inverted elements.""" return [not self._get(i) for i in range(len(self))] class LimitView(BasicView): """Array-like view of parameter limits.""" def __init__(self, minuit: Any): # Users should not call this __init__, instances are created by the library super(LimitView, self).__init__(minuit, 1) def _get(self, i: int) -> Tuple[float, float]: p = self._minuit._last_state[i] return ( p.lower_limit if p.has_lower_limit else -np.inf, p.upper_limit if p.has_upper_limit else np.inf, ) def _set(self, i: int, arg: UserBound) -> None: state = self._minuit._last_state val = state[i].value err = state[i].error # changing limits is a cheap operation, start from clean state state.remove_limits(i) low, high = _normalize_limit(arg) if low != -np.inf and high != np.inf: # both must be set if low == high: state.fix(i) else: state.set_limits(i, low, high) elif low != -np.inf: # lower limit must be set state.set_lower_limit(i, low) elif high != np.inf: # lower limit must be set state.set_upper_limit(i, high) # bug in Minuit2: must set parameter value and error again after changing limits if val < low: val = low elif val > high: val = high state.set_value(i, val) state.set_error(i, err) def _normalize_limit(lim: UserBound) -> Tuple[float, float]: if lim is None: return (-np.inf, np.inf) a, b = lim if a is None: a = -np.inf if b is None: b = np.inf if a > b: raise ValueError(f"limit {lim!r} is invalid") return a, b class Matrix(np.ndarray): """ Enhanced Numpy ndarray. Works like a normal ndarray in computations, but also supports pretty printing in ipython and Jupyter notebooks. Elements can be accessed via indices or parameter names. """ __slots__ = ("_var2pos",) def __new__(cls, parameters: Union[Dict, Tuple]) -> Any: # noqa D102 # Users should not call __new__, instances are created by the library if isinstance(parameters, dict): var2pos = parameters elif isinstance(parameters, tuple): var2pos = {x: i for i, x in enumerate(parameters)} else: raise TypeError("parameters must be tuple or dict") n = len(parameters) obj = super().__new__(cls, (n, n)) obj._var2pos = var2pos return obj def __array_finalize__(self, obj: Any) -> None: """For internal use.""" if obj is None: self._var2pos: Dict[str, int] = {} else: self._var2pos = getattr(obj, "_var2pos", {}) def __getitem__( # type:ignore self, key: Union[Key, Tuple[Key, Key], Iterable[Key], NDArray], ) -> NDArray: """Get matrix element at key.""" var2pos = self._var2pos def trafo(key): if isinstance(key, str): return var2pos[key] if isinstance(key, slice): return slice(trafo(key.start), trafo(key.stop), key.step) if isinstance(key, tuple): return tuple(trafo(k) for k in key) return key if isinstance(key, slice): # slice returns square matrix sl = trafo(key) return super().__getitem__((sl, sl)) if isinstance(key, (str, tuple)): return super().__getitem__(trafo(key)) if isinstance(key, Iterable) and not isinstance(key, np.ndarray): # iterable returns square matrix index2 = [trafo(k) for k in key] # type:ignore t = super().__getitem__(index2).T return np.ndarray.__getitem__(t, index2).T return super().__getitem__(key) def to_dict(self) -> Dict[Tuple[str, str], float]: """ Convert matrix to dict. Since the matrix is symmetric, the dict only contains the upper triangular matrix. """ names = tuple(self._var2pos) d = {} for i, pi in enumerate(names): for j in range(i, len(names)): pj = names[j] d[pi, pj] = float(self[i, j]) return d def to_table(self) -> Tuple[List[List[str]], Tuple[str, ...]]: """ Convert matrix to tabular format. The output is consumable by the external `tabulate `_ module. Examples -------- >>> import tabulate as tab >>> from iminuit import Minuit >>> m = Minuit(lambda x, y: x ** 2 + y ** 2, x=1, y=2).migrad() >>> tab.tabulate(*m.covariance.to_table()) x y -- --- --- x 1 -0 y -0 4 """ names = tuple(self._var2pos) # type:ignore nums = _repr_text.matrix_format(self) tab = [] n = len(self) for i, name in enumerate(names): tab.append([name] + [nums[n * i + j] for j in range(n)]) return tab, names def correlation(self): """ Compute and return correlation matrix. If the matrix is already a correlation matrix, this effectively returns a copy of the original matrix. """ a = self.copy() d = np.diag(a) ** 0.5 a /= np.outer(d, d) + 1e-100 return a def __repr__(self): """Get detailed text representation.""" return super(Matrix, self).__str__() def __str__(self): """Get user-friendly text representation.""" if self.ndim != 2: return repr(self) return _repr_text.matrix(self) def _repr_html_(self): return _repr_html.matrix(self) def _repr_pretty_(self, p, cycle): if cycle: p.text("") else: p.text(str(self)) # ndarray uses __reduce__ for pickling instead of __getstate__ def __reduce__(self): """Get representation for pickling and copying.""" restore, args, state = super().__reduce__() return restore, args, (state, self._var2pos) def __setstate__(self, state): """Restore from pickled state.""" state, self._var2pos = state super().__setstate__(state) class FMin: """ Function minimum view. This object provides detailed metadata about the function minimum. Inspect this to check what exactly happened if the fit did not converge. Use the attribute :attr:`iminuit.Minuit.fmin` to get the best fit values, their uncertainties, or the function value at the minimum. For convenience, you can also get a basic OK from :class:`iminuit.Minuit` with the methods :attr:`iminuit.Minuit.valid` and :attr:`iminuit.Minuit.accurate`. See Also -------- :attr:`iminuit.Minuit.values` :attr:`iminuit.Minuit.errors` :attr:`iminuit.Minuit.merrors` :attr:`iminuit.Minuit.covariance` :attr:`iminuit.Minuit.fval` :attr:`iminuit.Minuit.valid` :attr:`iminuit.Minuit.accurate` """ __slots__ = ( "_src", "_algorithm", "_has_parameters_at_limit", "_nfcn", "_ngrad", "_ndof", "_edm_goal", "_time", ) def __init__( self, fmin: Any, algorithm: str, nfcn: int, ngrad: int, ndof: int, edm_goal: float, time: float, ): # Users should not call this __init__, instances are created by the library self._src = fmin self._algorithm = algorithm self._has_parameters_at_limit = False for mp in fmin.state: if mp.is_fixed or not mp.has_limits: continue v = mp.value e = mp.error lb = mp.lower_limit if mp.has_lower_limit else -np.inf ub = mp.upper_limit if mp.has_upper_limit else np.inf # the 0.5 error threshold is somewhat arbitrary self._has_parameters_at_limit |= min(v - lb, ub - v) < 0.5 * e self._nfcn = nfcn self._ngrad = ngrad self._ndof = ndof self._edm_goal = edm_goal self._time = time @property def algorithm(self) -> str: """Get algorithm that was used to compute the function minimum.""" return self._algorithm @property def edm(self) -> float: """ Get Estimated Distance to Minimum. Minuit uses this criterion to determine whether the fit converged. It depends on the gradient and the Hessian matrix. It measures how well the current second order expansion around the function minimum describes the function, by taking the difference between the predicted (based on gradient and Hessian) function value at the minimum and the actual value. """ return self._src.edm # type:ignore @property def edm_goal(self) -> float: """ Get EDM threshold value for stopping the minimization. The threshold is allowed to be violated by a factor of 10 in some situations. """ return self._edm_goal @property def fval(self) -> float: """Get cost function value at the minimum.""" return self._src.fval # type:ignore @property def reduced_chi2(self) -> float: """ Get χ²/ndof of the fit. This returns NaN if the cost function is unbinned, errordef is not 1, or if the cost function does not report the degrees of freedom. """ if np.isfinite(self._ndof) and self._ndof > 0 and self.errordef == 1: return self.fval / self._ndof return np.nan @property def has_parameters_at_limit(self) -> bool: """ Return whether any bounded parameter was fitted close to a bound. The estimated error for the affected parameters is usually off. May be an indication to remove or loosen the limits on the affected parameter. """ return self._has_parameters_at_limit @property def nfcn(self) -> int: """Get number of function calls so far.""" return self._nfcn @property def ngrad(self) -> int: """Get number of function gradient calls so far.""" return self._ngrad @property def is_valid(self) -> bool: """ Return whether Migrad converged successfully. For it to return True, the following conditions need to be fulfilled: - :attr:`has_reached_call_limit` is False - :attr:`is_above_max_edm` is False Note: The actual verdict is computed inside the Minuit2 C++ code, so we cannot guarantee that is_valid is exactly equivalent to these conditions. """ return self._src.is_valid # type:ignore @property def has_valid_parameters(self) -> bool: """ Return whether parameters are valid. This is the same as :attr:`is_valid` and only kept for backward compatibility. """ return self.is_valid @property def has_accurate_covar(self) -> bool: """ Return whether the covariance matrix is accurate. While Migrad runs, it computes an approximation to the current Hessian matrix. If the strategy is set to 0 or if the fit did not converge, the inverse of this approximation is returned instead of the inverse of the accurately computed Hessian matrix. This property returns False if the approximation has been returned instead of an accurate matrix computed by the Hesse method. """ return self._src.has_accurate_covar # type:ignore @property def has_posdef_covar(self) -> bool: """ Return whether the Hessian matrix is positive definite. This must be the case if the extremum is a minimum, otherwise it is a saddle point. If it returns False, the fitted result may be correct, but the reported uncertainties are false. This may affect some parameters or all of them. Possible causes: * Model contains redundanted parameters that are 100% correlated. Fix: remove the parameters that are 100% correlated. * Cost function is not computed in double precision. Fix: try adjusting :attr:`iminuit.Minuit.precision` or change the cost function to compute in double precision. * Cost function is not analytical near the minimum. Fix: change the cost function to something analytical. Functions are not analytical if: * It does computations based on (pseudo)random numbers. * It contains vertical steps, for example from code like this:: if cond: return value1 else: return value2 """ return self._src.has_posdef_covar # type:ignore @property def has_made_posdef_covar(self) -> bool: """ Return whether the matrix was forced to be positive definite. While Migrad runs, it computes an approximation to the current Hessian matrix. It can happen that this approximation is not positive definite, but that is required to compute the next Newton step. Migrad then adds an appropriate diagonal matrix to enforce positive definiteness. If the fit has converged successfully, this should always return False. If Minuit forced the matrix to be positive definite, the parameter uncertainties are false, see :attr:`has_posdef_covar` for more details. """ return self._src.has_made_posdef_covar # type:ignore @property def hesse_failed(self) -> bool: """Return whether the last call to Hesse failed.""" return self._src.hesse_failed # type:ignore @property def has_covariance(self) -> bool: """ Return whether a covariance matrix was computed at all. This is false if the Simplex minimization algorithm was used instead of Migrad, in which no approximation to the Hessian is computed. """ return self._src.has_covariance # type:ignore @property def is_above_max_edm(self) -> bool: """ Return whether the EDM value is below the convergence threshold. Returns True, if the fit did not converge; otherwise returns False. """ return self._src.is_above_max_edm # type:ignore @property def has_reached_call_limit(self) -> bool: """ Return whether Migrad exceeded the allowed number of function calls. Returns True true, the fit was stopped before convergence was reached; otherwise returns False. """ return self._src.has_reached_call_limit # type:ignore @property def errordef(self) -> float: """Equal to the value of :attr:`iminuit.Minuit.errordef` when Migrad ran.""" return self._src.errordef # type:ignore @property def time(self) -> float: """Runtime of the last algorithm.""" return self._time def __eq__(self, other: object) -> bool: """Return True if all attributes are equal.""" def relaxed_equal(k: str, a: object, b: object) -> bool: a = getattr(a, k) b = getattr(b, k) if isinstance(a, float) and np.isnan(a): return np.isnan(b) # type:ignore return a == b # type:ignore return all(relaxed_equal(k, self, other) for k in self.__slots__) def __repr__(self) -> str: """Get detailed text representation.""" s = " str: """Get user-friendly text representation.""" return _repr_text.fmin(self) # type:ignore def _repr_html_(self) -> str: return _repr_html.fmin(self) # type:ignore def _repr_pretty_(self, p: Any, cycle: bool) -> None: if cycle: p.text("") else: p.text(str(self)) class Param: """Data object for a single Parameter.""" __slots__ = ( "number", "name", "value", "error", "merror", "is_const", "is_fixed", "lower_limit", "upper_limit", ) def __init__( self, *args: Union[int, str, float, Optional[Tuple[float, float]], bool], ): # Users should not call this __init__, instances are created by the library assert len(args) == len(self.__slots__) for k, arg in zip(self.__slots__, args): setattr(self, k, arg) def __eq__(self, other: object) -> bool: """Return True if all values are equal.""" return all(getattr(self, k) == getattr(other, k) for k in self.__slots__) def __repr__(self) -> str: """Get detailed text representation.""" pairs = [] for k in self.__slots__: v = getattr(self, k) pairs.append(f"{k}={v!r}") return "Param(" + ", ".join(pairs) + ")" @property def has_limits(self): """Query whether the parameter has an lower or upper limit.""" return self.has_lower_limit or self.has_upper_limit @property def has_lower_limit(self): """Query whether parameter has a lower limit.""" return self.lower_limit is not None @property def has_upper_limit(self): """Query whether parameter has an upper limit.""" return self.upper_limit is not None def __str__(self) -> str: """Get user-friendly text representation.""" return _repr_text.params([self]) # type:ignore def _repr_pretty_(self, p: Any, cycle: bool) -> None: if cycle: p.text("Param(...)") else: p.text(str(self)) class Params(tuple): """Tuple-like holder of parameter data objects.""" __slots__ = () def _repr_html_(self): return _repr_html.params(self) def to_table(self): """ Convert parameter data to a tabular format. The output is consumable by the external `tabulate `_ module. Examples -------- >>> import tabulate as tab >>> from iminuit import Minuit >>> m = Minuit(lambda x, y: x ** 2 + (y / 2) ** 2 + 1, x=0, y=0) >>> m.fixed["x"] = True >>> m.migrad().minos() >>> tab.tabulate(*m.params.to_table()) pos name value error error- error+ limit- limit+ fixed ----- ------ ------- ------- -------- -------- -------- -------- ------- 0 x 0 0.1 yes 1 y 0 1.4 -1.0 1.0 """ header = [ "pos", "name", "value", "error", "error-", "error+", "limit-", "limit+", "fixed", ] tab = [] for i, mp in enumerate(self): name = mp.name row = [i, name] me = mp.merror if me: val, err, mel, meu = _repr_text.pdg_format(mp.value, mp.error, *me) else: val, err = _repr_text.pdg_format(mp.value, mp.error) mel = "" meu = "" row += [ val, err, mel, meu, f"{mp.lower_limit}" if mp.lower_limit is not None else "", f"{mp.upper_limit}" if mp.upper_limit is not None else "", "yes" if mp.is_fixed else "", ] tab.append(row) return tab, header def __getitem__(self, key): """Get item at key, which can be an index or a parameter name.""" if isinstance(key, str): for i, p in enumerate(self): if p.name == key: break key = i return super(Params, self).__getitem__(key) def __str__(self): """Get user-friendly text representation.""" return _repr_text.params(self) def _repr_pretty_(self, p, cycle): if cycle: p.text("Params(...)") else: p.text(str(self)) class MError: """ Minos data object. Attributes ---------- number : int Parameter index. name : str Parameter name. lower : float Lower error. upper : float Upper error. is_valid : bool Whether Minos computation was successful. lower_valid : bool Whether downward scan was successful. upper_valid : bool Whether upward scan was successful. at_lower_limit : bool Whether scan reached lower limit. at_upper_limit : bool Whether scan reached upper limit. at_lower_max_fcn : bool Whether allowed number of function evaluations was exhausted. at_upper_max_fcn : bool Whether allowed number of function evaluations was exhausted. lower_new_min : float Parameter value for new minimum, if one was found in downward scan. upper_new_min : float Parameter value for new minimum, if one was found in upward scan. nfcn : int Number of function calls. min : float Function value at the new minimum. """ __slots__ = ( "number", "name", "lower", "upper", "is_valid", "lower_valid", "upper_valid", "at_lower_limit", "at_upper_limit", "at_lower_max_fcn", "at_upper_max_fcn", "lower_new_min", "upper_new_min", "nfcn", "min", ) def __init__(self, *args: Union[int, str, float, bool]): # Users should not call this __init__, instances are created by the library assert len(args) == len(self.__slots__) for k, arg in zip(self.__slots__, args): setattr(self, k, arg) def __eq__(self, other: object) -> bool: """Return True if all values are equal.""" return all(getattr(self, k) == getattr(other, k) for k in self.__slots__) def __repr__(self) -> str: """Get detailed text representation.""" s = " str: """Get user-friendly text representation.""" return _repr_text.merrors({None: self}) # type:ignore def _repr_html_(self) -> str: return _repr_html.merrors({None: self}) # type:ignore def _repr_pretty_(self, p: Any, cycle: bool) -> None: if cycle: p.text("") else: p.text(str(self)) class MErrors(OrderedDict): """Dict-like map from parameter name to Minos result object.""" __slots__ = () def _repr_html_(self): return _repr_html.merrors(self) def __repr__(self): """Get detailed text representation.""" return "" def __str__(self): """Get user-friendly text representation.""" return _repr_text.merrors(self) def _repr_pretty_(self, p, cycle): if cycle: p.text("") else: p.text(str(self)) def __getitem__(self, key): """Get item at key, which can be an index or a parameter name.""" if isinstance(key, int): if key < 0: key += len(self) if key < 0 or key >= len(self): raise IndexError("index out of range") for i, k in enumerate(self): if i == key: break key = k return OrderedDict.__getitem__(self, key) @_deprecated.deprecated("use jacobi.propagate instead from jacobi library") def propagate( fn: Callable, x: Collection[float], cov: Collection[Collection[float]], ) -> Tuple[NDArray, NDArray]: """ Numerically propagates the covariance into a new space. This function is deprecated and will be removed. Please use jacobi.propagate from the jacobi library, which is more accurate. The signatures of the two functions are compatible, so it is a drop-in replacement. Parameters ---------- fn: callable Vectorized function that computes y = fn(x). x: array-like with shape (N,) Input vector. cov: array-like with shape (N, N) Covariance matrix of input vector. Returns ------- y, ycov y is the result of fn(x) ycov is the propagated covariance matrix. """ from scipy.optimize import approx_fprime vx = np.atleast_1d(x) # type:ignore if np.ndim(cov) != 2: # type:ignore raise ValueError("cov must be 2D array-like") vcov = np.atleast_2d(cov) # type:ignore if vcov.shape[0] != vcov.shape[1]: raise ValueError("cov must have shape (N, N)") tol = 1e-10 dx = (np.diag(vcov) * tol) ** 0.5 if not np.all(dx >= 0): raise ValueError("diagonal elements of covariance matrix must be non-negative") y = fn(vx) jac = np.atleast_2d(approx_fprime(vx, fn, dx)) ycov = np.einsum("ij,kl,jl", jac, jac, vcov) return y, np.squeeze(ycov) if np.ndim(y) == 0 else ycov class _Timer: def __init__(self, fmin): self.value = fmin.time if fmin else 0.0 def __enter__(self): self.value += monotonic() def __exit__(self, *args): self.value = monotonic() - self.value @_deprecated.deprecated( "Use of ``func_code`` attribute to declare parameters is deprecated. " "Use ``_parameters`` instead, which is a dict of parameter names to limits." ) def make_func_code(params: Collection[str]) -> Namespace: """ Make a func_code object to fake a function signature. Example:: def f(a, b): ... f.func_code = make_func_code(["x", "y"]) """ return Namespace(co_varnames=tuple(params), co_argcount=len(params)) def make_with_signature( callable: Callable, *varnames: str, **replacements: str ) -> Callable: """ Return new callable with altered signature. Parameters ---------- callable: Original callable whose signature shall be changed. *varnames: sequence of str Replace the first N argument names with these. **replacements: mapping of str to str, optional Replace old argument name (key) with new argument name (value). Returns ------- callable with new argument names. """ pars = describe(callable, annotations=True) if pars: args = list(pars) n = len(varnames) if n > len(args): raise ValueError("varnames longer than original signature") args[:n] = varnames for old, new in replacements.items(): i = args.index(old) args[i] = new pars = {new: pars[old] for (new, old) in zip(args, pars)} class Caller: def __init__(self, parameters): self._parameters = parameters def __call__(self, *args: object) -> object: return callable(*args) return Caller(pars) def merge_signatures( callables: Iterable[Callable], annotations: bool = False ) -> Tuple[Any, List[Tuple[int, ...]]]: """ Merge signatures of callables with positional arguments. This is best explained by an example:: def f(x, y, z): ... def g(x, p): ... parameters, mapping = merge_signatures(f, g) # parameters is ('x', 'y', 'z', 'p') # mapping is ((0, 1, 2), (0, 3)) Parameters ---------- callables : Callables whose parameters can be extracted with :func:`describe`. annotations : bool, optional Whether to return the annotions. Default is False, for backward compatibility. Returns ------- tuple(parameters, mapping) parameters is the tuple of the merged parameter names. mapping contains the mapping of parameters indices from the merged signature to the original signatures. """ args: List[str] = [] anns: List[Optional[Tuple[float, float]]] = [] mapping = [] for f in callables: amap = [] for i, (k, ann) in enumerate(describe(f, annotations=True).items()): if k in args: amap.append(args.index(k)) else: amap.append(len(args)) args.append(k) anns.append(ann) mapping.append(tuple(amap)) if annotations: return {k: a for (k, a) in zip(args, anns)}, mapping return args, mapping @overload def describe(callable: Callable) -> List[str]: ... # pragma: no cover @overload def describe( callable: Callable, annotations: bool ) -> Dict[str, Optional[Tuple[float, float]]]: ... # pragma: no cover def describe(callable, *, annotations=False): """ Attempt to extract the function argument names and annotations. Parameters ---------- callable : callable Callable whose parameters should be extracted. annotations : bool, optional Whether to also extract annotations. Default is false. Returns ------- list or dict If annotate is False, return list of strings with the parameters names if successful and an empty list otherwise. If annotations is True, return a dict, which maps parameter names to annotations. For parameters without annotations, the dict maps to None. Notes ----- Parameter names are extracted with the following three methods, which are attempted in order. The first to succeed determines the result. 1. Using ``obj._parameters``, which is a dict that makes parameter names to parameter limits or None if the parameter has to limits. Users are encouraged to use this mechanism to provide signatures for objects that otherwise would not have a detectable signature. Example:: def f(*args): # no signature x, y = args return (x - 2) ** 2 + (y - 3) ** 2 f._parameters = {"x": None, "y": (1, 4)} Here, the first parameter is declared to have no limits (values from minus to plus infinity are allowed), while the second parameter is declared to have limits, it cannot be smaller than 1 or larger than 4. Note: In the past, the ``func_code`` attribute was used for a similar purpose as ``_parameters``. It is still supported for legacy code, but should not be used anymore in new code, since it does not support declaring parameter limits. If an objects has a ``func_code`` attribute, it is used to detect the parameters. Example:: from iminuit.util import make_func_code def f(*args): # no signature x, y = args return (x - 2) ** 2 + (y - 3) ** 2 # deprecated, make_func_code will raise a warning f.func_code = make_func_code(("x", "y")) 2. Using :func:`inspect.signature`. The :mod:`inspect` module provides a general function to extract the signature of a Python callable. It works on most callables, including Functors like this:: class MyLeastSquares: def __call__(self, a, b): ... Limits are supported via annotations, using the Annotated type that was introduced in Python-3.9 and can be obtained from the external package typing_extensions in Python-3.8. In the following example, the second parameter has a lower limit at 0, because it must be positive:: from typing import Annotated def my_cost_function(a: float, b: Annotated[float, 0:]): ... There are no standard annotations yet at the time of this writing. iminuit supports the annotations Gt, Ge, Lt, Le from the external package annotated-types, and interprets slice notation as limits. 3. Using the docstring. The docstring is textually parsed to detect the parameter names. This requires that a docstring is present which follows the Python standard formatting for function signatures. Ambiguous cases with positional and keyword argument are handled in the following way:: # describe returns [a, b]; # *args and **kwargs are ignored def fcn(a, b, *args, **kwargs): ... # describe returns [a, b, c]; # positional arguments with default values are detected def fcn(a, b, c=1): ... """ if _address_of_cfunc(callable) != 0: return {} if annotations else [] args = ( getattr(callable, "_parameters", {}) or _arguments_from_func_code(callable) or _parameters_from_inspect(callable) or _arguments_from_docstring(callable) ) if annotations: return args # for backward-compatibility return list(args) def _arguments_from_func_code(callable): # Check (faked) f.func_code; for backward-compatibility with iminuit-1.x if hasattr(callable, "func_code"): # cannot warn about deprecation here, since numba.njit also uses .func_code fc = callable.func_code return {x: None for x in fc.co_varnames[: fc.co_argcount]} return {} def _get_limit(annotation: Union[type, Annotated[float, Any], str]): from iminuit import typing if isinstance(annotation, str): # This provides limited support for string annotations, which # have a lot of problems, see https://peps.python.org/pep-0649. try: annotation = eval(annotation, None, typing.__dict__) except NameError: # We ignore unknown annotations to fix issue #846. # I cannot replicate here what inspect.signature(..., eval_str=True) does. # I need a dict with the global objects at the call site of describe, but # it is not globals(). Anyway, when using strings, only the annotations # from the package annotated-types are supported. pass if annotation == inspect.Parameter.empty: return None if get_origin(annotation) is not Annotated: return None tp, *constraints = get_args(annotation) assert tp is float lim = [-np.inf, np.inf] for c in constraints: if isinstance(c, slice): if c.start is not None: lim[0] = c.start if c.stop is not None: lim[1] = c.stop # Minuit does not distinguish between closed and open intervals. # We use a chain of ifs so that the code also works with the # `Interval` class, which contains several of those attributes # and which can be None. gt = getattr(c, "gt", None) ge = getattr(c, "ge", None) lt = getattr(c, "lt", None) le = getattr(c, "le", None) if gt is not None: lim[0] = gt if ge is not None: lim[0] = ge if lt is not None: lim[1] = lt if le is not None: lim[1] = le return tuple(lim) def _parameters_from_inspect(callable): try: signature = inspect.signature(callable) except ValueError: # raised when used on built-in function return {} r = {} for name, par in signature.parameters.items(): # stop when variable number of arguments is encountered if par.kind is inspect.Parameter.VAR_POSITIONAL: break # stop when keyword argument is encountered if par.kind is inspect.Parameter.VAR_KEYWORD: break r[name] = _get_limit(par.annotation) return r def _arguments_from_docstring(callable): doc = inspect.getdoc(callable) if doc is None: return {} # Examples of strings we want to parse: # min(iterable, *[, default=obj, key=func]) -> value # min(arg1, arg2, *args, *[, key=func]) -> value # Foo.bar(self, int ncall_me =10000, [resume=True, int nsplit=1]) try: # function wrapper functools.partial does not offer __name__, # we cannot extract the signature in this case name = callable.__name__ except AttributeError: return {} token = name + "(" start = doc.find(token) if start < 0: return {} start += len(token) nbrace = 1 for ich, ch in enumerate(doc[start:]): if ch == "(": nbrace += 1 elif ch == ")": nbrace -= 1 if nbrace == 0: break items = [x.strip(" []") for x in doc[start : start + ich].split(",")] # strip self if callable is a class method if items[0] == "self": items = items[1:] # "iterable", "*", "default=obj", "key=func" # "arg1", "arg2", "*args", "*", "key=func" # "int ncall_me =10000", "resume=True", "int nsplit=1" try: i = items.index("*args") items = items[:i] except ValueError: pass # "iterable", "*", "default=obj", "key=func" # "arg1", "arg2", "*", "key=func" # "int ncall_me =10000", "resume=True", "int nsplit=1" def extract(s: str) -> str: a = s.find(" ") b = s.find("=") if a < 0: a = 0 if b < 0: b = len(s) return s[a:b].strip() # "iterable", "default", "key" # "arg1", "arg2", "key" # "ncall_me", "resume", "nsplit" return {extract(x): None for x in items if x != "*"} def _guess_initial_step(val: float) -> float: return 1e-2 * abs(val) if val != 0 else 1e-1 # heuristic def _key2index_from_slice(var2pos: Dict[str, int], key: slice) -> List[int]: start = var2pos[key.start] if isinstance(key.start, str) else key.start stop = var2pos[key.stop] if isinstance(key.stop, str) else key.stop start, stop, step = slice(start, stop, key.step).indices(len(var2pos)) return list(range(start, stop, step)) def _key2index_item(var2pos: Dict[str, int], key: Union[str, int]) -> int: if isinstance(key, str): return var2pos[key] i = key if i < 0: i += len(var2pos) if i < 0 or i >= len(var2pos): raise IndexError return i def _key2index( var2pos: Dict[str, int], key: Key, ) -> Union[int, List[int]]: if key is ...: return list(range(len(var2pos))) if isinstance(key, slice): return _key2index_from_slice(var2pos, key) if not isinstance(key, str) and isinstance(key, Iterable): # convert boolean masks into list of indices if isinstance(key[0], bool): key = [k for k in range(len(var2pos)) if key[k]] return [_key2index_item(var2pos, k) for k in key] return _key2index_item(var2pos, key) def _address_of_cfunc(fcn: Any) -> int: from ctypes import ( c_void_p, c_double, c_uint32, POINTER, CFUNCTYPE, cast as ctypes_cast, ) c_sig = CFUNCTYPE(c_double, c_uint32, POINTER(c_double)) fcn = getattr(fcn, "ctypes", None) if isinstance(fcn, c_sig): return ctypes_cast(fcn, c_void_p).value # type: ignore return 0 def _iterate(x): if not isinstance(x, Iterable): yield x else: for xi in x: yield xi def _replace_none(x, v): if x is None: return v return x # poor-mans progressbar class ProgressBar: """ Simple progress bar. Renders as nice HTML progressbar in Jupyter notebooks. """ value: int = 0 max_value: int def _update(self, fraction): self._out.write(f"\r{100 * fraction:.0f} %") self._out.flush() def _finish(self): self._out.write("\r ") self._out.flush() def __init__(self, max_value): """ Initialize bar. Parameters ---------- max_value: int Total number of entries. """ self.max_value = max_value self._out = sys.stdout try: with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) # DeprecationWarning: Jupyter is migrating its paths ... from ipykernel.iostream import OutStream if isinstance(self._out, OutStream): from IPython.display import display, HTML self._update = lambda v: display( HTML( f" " f"{100 * v:.0f} %" ), clear=True, ) self._finish = lambda: display(HTML(""), clear=True) except ModuleNotFoundError: pass self._update(self.value) def __enter__(self, *args): """Noop.""" return self def __exit__(self, *args): """Clean up the bar.""" self._finish() def __add__(self, v): """Increment progress.""" self.value += v self._update(self.value / self.max_value) return self def _histogram_segments(mask, xe, masked): assert masked.ndim == 1 if mask is None: return [(masked, xe)] segments = [] a = 0 b = 0 am = 0 n = len(mask) while a < n: if not mask[a]: a += 1 continue b = a + 1 while b < n and mask[b]: b += 1 segments.append((masked[am : am + b - a], xe[a : b + 1])) am += b - a a = b + 1 return segments def _smart_sampling(f, xmin, xmax, start=5, tol=5e-3, maxiter=20, maxtime=10): t0 = monotonic() x = np.linspace(xmin, xmax, start) ynew = f(x) ymin = np.min(ynew) ymax = np.max(ynew) y = {xi: yi for (xi, yi) in zip(x, ynew)} a = x[:-1] b = x[1:] niter = 0 while len(a): niter += 1 if niter > maxiter: msg = ( f"Iteration limit {maxiter} in smart sampling reached, " f"produced {len(y)} points" ) warnings.warn(msg, RuntimeWarning) break if monotonic() - t0 > maxtime: msg = ( f"Time limit {maxtime} in smart sampling reached, " f"produced {len(y)} points" ) warnings.warn(msg, RuntimeWarning) break xnew = 0.5 * (a + b) ynew = f(xnew) ymin = min(ymin, np.min(ynew)) ymax = max(ymax, np.max(ynew)) for xi, yi in zip(xnew, ynew): y[xi] = yi yint = 0.5 * ( np.fromiter((y[ai] for ai in a), float) + np.fromiter((y[bi] for bi in b), float) ) dy = np.abs(ynew - yint) dx = np.abs(b - a) # in next iteration, handle intervals which do not # pass interpolation test and are not too narrow mask = (dy > tol * (ymax - ymin)) & (dx > tol * abs(xmax - xmin)) a = a[mask] b = b[mask] xnew = xnew[mask] a = np.append(a, xnew) b = np.append(xnew, b) xy = list(y.items()) xy.sort() return np.transpose(xy) def _detect_log_spacing(x: NDArray) -> bool: # x should never contain NaN x = np.sort(x) if x[0] <= 0: return False d_lin = np.diff(x) d_log = np.diff(np.log(x)) lin_rel_std = np.std(d_lin) / np.mean(d_lin) log_rel_std = np.std(d_log) / np.mean(d_log) return log_rel_std < lin_rel_std iminuit-2.24.0/src/iminuit/warnings.py0000644000000000000000000000057014332717401014656 0ustar00"""Warnings used by iminuit.""" class IMinuitWarning(RuntimeWarning): """Generic iminuit warning.""" class OptionalDependencyWarning(IMinuitWarning): """Feature requires an optional external package.""" class HesseFailedWarning(IMinuitWarning): """HESSE failed warning.""" class PerformanceWarning(UserWarning): """Warning about performance issues.""" iminuit-2.24.0/src/lasymmatrix.cpp0000644000000000000000000000074114332717401014054 0ustar00#include "lasymmatrix.hpp" namespace py = pybind11; using namespace ROOT::Minuit2; py::tuple lasymmatrix2py(const LASymMatrix& self) { py::list ls; for (unsigned i = 0; i < self.size(); ++i) ls.append(self.Data()[i]); return py::make_tuple(self.Nrow(), ls); } LASymMatrix py2lasymmatrix(py::tuple tp) { LASymMatrix v(tp[0].cast()); auto ls = tp[1].cast(); for (unsigned i = 0; i < v.size(); ++i) v.Data()[i] = ls[i].cast(); return v; } iminuit-2.24.0/src/lasymmatrix.hpp0000644000000000000000000000043114332717401014055 0ustar00#ifndef IMINUIT_LASYMMATRIX #define IMINUIT_LASYMMATRIX #include #include namespace py = pybind11; using namespace ROOT::Minuit2; py::tuple lasymmatrix2py(const LASymMatrix& self); LASymMatrix py2lasymmatrix(py::tuple tp); #endif iminuit-2.24.0/src/lavector.cpp0000644000000000000000000000057714332717401013330 0ustar00#include "lavector.hpp" namespace py = pybind11; using namespace ROOT::Minuit2; py::list lavector2py(const LAVector& self) { py::list ls; for (unsigned i = 0; i < self.size(); ++i) ls.append(self.Data()[i]); return ls; } LAVector py2lavector(py::list ls) { LAVector v(ls.size()); for (unsigned i = 0; i < v.size(); ++i) v.Data()[i] = ls[i].cast(); return v; } iminuit-2.24.0/src/lavector.hpp0000644000000000000000000000037614332717401013332 0ustar00#ifndef IMINUIT_LAVECTOR #define IMINUIT_LAVECTOR #include #include namespace py = pybind11; using namespace ROOT::Minuit2; py::list lavector2py(const LAVector& v); LAVector py2lavector(py::list ls); #endif iminuit-2.24.0/src/machineprecision.cpp0000644000000000000000000000167614332717401015032 0ustar00#include #include #include namespace ROOT { namespace Minuit2 { bool operator==(const MnMachinePrecision& a, const MnMachinePrecision& b) { return a.Eps() == b.Eps() && a.Eps2() == b.Eps2(); } } // namespace Minuit2 } // namespace ROOT namespace py = pybind11; using namespace ROOT::Minuit2; void bind_machineprecision(py::module m) { py::class_(m, "MnMachinePrecision") .def(py::init<>()) .def_property("eps", &MnMachinePrecision::Eps, &MnMachinePrecision::SetPrecision) .def_property_readonly("eps2", &MnMachinePrecision::Eps2) .def(py::self == py::self) .def(py::pickle( [](const MnMachinePrecision& self) { return py::make_tuple(self.Eps()); }, [](py::tuple tp) { MnMachinePrecision p; p.SetPrecision(tp[0].cast()); return p; })) ; } iminuit-2.24.0/src/main.cpp0000644000000000000000000000202114332717401012417 0ustar00#include namespace py = pybind11; void bind_application(py::module); void bind_contours(py::module); void bind_fcn(py::module); void bind_functionminimum(py::module); void bind_hesse(py::module); void bind_machineprecision(py::module); void bind_migrad(py::module); void bind_minimumstate(py::module); void bind_minos(py::module); void bind_minuitparameter(py::module); void bind_print(py::module); void bind_scan(py::module); void bind_simplex(py::module); void bind_strategy(py::module); void bind_usercovariance(py::module); void bind_userparameterstate(py::module); void bind_usertransformation(py::module); PYBIND11_MODULE(_core, m) { bind_application(m); bind_contours(m); bind_fcn(m); bind_functionminimum(m); bind_hesse(m); bind_machineprecision(m); bind_migrad(m); bind_minimumstate(m); bind_minos(m); bind_minuitparameter(m); bind_print(m); bind_scan(m); bind_simplex(m); bind_strategy(m); bind_usercovariance(m); bind_userparameterstate(m); bind_usertransformation(m); } iminuit-2.24.0/src/migrad.cpp0000644000000000000000000000135514332717401012747 0ustar00#include "fcn.hpp" #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; MnMigrad init(const FCN& fcn, const MnUserParameterState& state, const MnStrategy& str) { if (fcn.grad_.is_none()) { return MnMigrad(static_cast(fcn), state, str); } return MnMigrad(static_cast(fcn), state, str); } void bind_migrad(py::module m) { py::class_(m, "MnMigrad") .def(py::init(&init), py::keep_alive<1, 2>()) .def("set_print_level", [](MnMigrad& self, int lvl) { return self.Minimizer().Builder().SetPrintLevel(lvl); }) ; } iminuit-2.24.0/src/minimumstate.cpp0000644000000000000000000000727414332717401014226 0ustar00#include "lasymmatrix.hpp" #include "lavector.hpp" #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; py::tuple par2py(const MinimumParameters& pars) { return py::make_tuple(lavector2py(pars.Vec()), lavector2py(pars.Dirin()), pars.Fval(), pars.IsValid(), pars.HasStepSize()); } MinimumParameters py2par(py::tuple tp) { static_assert(std::is_standard_layout(), ""); struct Layout { MnAlgebraicVector fParameters; MnAlgebraicVector fStepSize; double fFVal; bool fValid; bool fHasStep; }; MinimumParameters pars(py2lavector(tp[0]), py2lavector(tp[1]), tp[2].cast()); // evil workaround, will segfault or cause UB if source layout changes auto& ptr = reinterpret_cast&>(pars); auto d = ptr.get(); d->fValid = tp[3].cast(); d->fHasStep = tp[4].cast(); return pars; } py::tuple err2py(const MinimumError& err) { return py::make_tuple(lasymmatrix2py(err.InvHessian()), err.Dcovar(), static_cast(err.GetStatus())); } MinimumError py2err(py::tuple tp) { auto status = static_cast(tp[2].cast()); if (status == MinimumError::MnPosDef) return MinimumError(py2lasymmatrix(tp[0]), tp[1].cast()); return MinimumError(py2lasymmatrix(tp[0]), status); } py::tuple grad2py(const FunctionGradient& g) { return py::make_tuple(lavector2py(g.Grad()), lavector2py(g.G2()), lavector2py(g.Gstep()), g.IsValid(), g.IsAnalytical()); } FunctionGradient py2grad(py::tuple tp) { const auto& gr = py2lavector(tp[0]); const auto& g2 = py2lavector(tp[1]); const auto& st = py2lavector(tp[2]); const auto& valid = tp[3].cast(); const auto& analytical = tp[4].cast(); if (valid) { if (analytical) return FunctionGradient{gr}; else return FunctionGradient{gr, g2, st}; } return FunctionGradient{gr.size()}; } void bind_minimumstate(py::module m) { py::class_(m, "MinimumState") .def(py::init()) .def_property_readonly( "vec", [](const MinimumState& self) { return lavector2py(self.Vec()); }) .def_property_readonly("fval", &MinimumState::Fval) .def_property_readonly("edm", &MinimumState::Edm) .def_property_readonly("nfcn", &MinimumState::NFcn) .def_property_readonly("is_valid", &MinimumState::IsValid) .def_property_readonly("has_parameters", &MinimumState::HasParameters) .def_property_readonly("has_covariance", &MinimumState::HasCovariance) .def(py::pickle( [](const MinimumState& self) { return py::make_tuple(par2py(self.Parameters()), err2py(self.Error()), grad2py(self.Gradient()), self.Edm(), self.NFcn()); }, [](py::tuple tp) { static_assert(std::is_standard_layout(), ""); struct Layout { MinimumParameters fParameters; MinimumError fError; FunctionGradient fGradient; double fEDM; int fNFcn; }; MinimumState st{0}; // evil workaround, will segfault or cause UB if source layout changes auto& ptr = reinterpret_cast&>(st); auto d = ptr.get(); d->fParameters = py2par(tp[0]); d->fError = py2err(tp[1]); d->fGradient = py2grad(tp[2]); d->fEDM = tp[3].cast(); d->fNFcn = tp[4].cast(); return st; })) ; } iminuit-2.24.0/src/minos.cpp0000644000000000000000000000272214332717401012630 0ustar00#include #include #include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void bind_minos(py::module m) { py::class_(m, "MinosError") .def_property_readonly("number", &MinosError::Parameter) .def_property_readonly("lower", &MinosError::Lower) .def_property_readonly("upper", &MinosError::Upper) .def_property_readonly("is_valid", &MinosError::IsValid) .def_property_readonly("lower_valid", &MinosError::LowerValid) .def_property_readonly("upper_valid", &MinosError::UpperValid) .def_property_readonly("at_lower_limit", &MinosError::AtLowerLimit) .def_property_readonly("at_upper_limit", &MinosError::AtUpperLimit) .def_property_readonly("at_lower_max_fcn", &MinosError::AtLowerMaxFcn) .def_property_readonly("at_upper_max_fcn", &MinosError::AtUpperMaxFcn) .def_property_readonly("lower_new_min", &MinosError::LowerNewMin) .def_property_readonly("upper_new_min", &MinosError::UpperNewMin) .def_property_readonly("nfcn", &MinosError::NFcn) .def_property_readonly("min", &MinosError::Min) ; py::class_(m, "MnMinos") .def(py::init()) // int ipar, unsigned maxcalls, double toler .def("__call__", &MnMinos::Minos) ; } iminuit-2.24.0/src/minuitparameter.cpp0000644000000000000000000000607414332717401014715 0ustar00#include "equal.hpp" #include #include #include namespace ROOT { namespace Minuit2 { bool operator==(const MinuitParameter& a, const MinuitParameter& b) { return a.Number() == b.Number() && a.GetName() == b.GetName() && a.Value() == b.Value() && a.Error() == b.Error() && a.IsConst() == b.IsConst() && a.IsFixed() == b.IsFixed() && a.HasLimits() == b.HasLimits() && a.HasLowerLimit() == b.HasLowerLimit() && a.HasUpperLimit() == b.HasUpperLimit() && nan_equal(a.LowerLimit(), b.LowerLimit()) && nan_equal(a.UpperLimit(), b.UpperLimit()); } } // namespace Minuit2 } // namespace ROOT namespace py = pybind11; using namespace ROOT::Minuit2; void bind_minuitparameter(py::module m) { py::class_(m, "MinuitParameter") .def_property_readonly("number", &MinuitParameter::Number) .def_property_readonly("name", &MinuitParameter::GetName) .def_property_readonly("value", &MinuitParameter::Value) .def_property_readonly("error", &MinuitParameter::Error) .def_property_readonly("is_const", &MinuitParameter::IsConst) .def_property_readonly("is_fixed", &MinuitParameter::IsFixed) .def_property_readonly("has_limits", &MinuitParameter::HasLimits) .def_property_readonly("has_lower_limit", &MinuitParameter::HasLowerLimit) .def_property_readonly("has_upper_limit", &MinuitParameter::HasUpperLimit) .def_property_readonly("lower_limit", &MinuitParameter::LowerLimit) .def_property_readonly("upper_limit", &MinuitParameter::UpperLimit) .def(py::self == py::self) .def(py::pickle( [](const MinuitParameter& self) { return py::make_tuple(self.Number(), self.GetName(), self.Value(), self.Error(), self.IsConst(), self.IsFixed(), self.LowerLimit(), self.UpperLimit(), self.HasLowerLimit(), self.HasUpperLimit()); }, [](py::tuple tp) { static_assert(std::is_standard_layout(), ""); if (tp.size() != 10) throw std::runtime_error("invalid state"); MinuitParameter p{tp[0].cast(), tp[1].cast(), tp[2].cast(), tp[3].cast()}; struct Layout { unsigned int fNum; double fValue; double fError; bool fConst; bool fFix; double fLoLimit; double fUpLimit; bool fLoLimValid; bool fUpLimValid; std::string fName; }; auto d = reinterpret_cast(&p); d->fConst = tp[4].cast(); d->fFix = tp[5].cast(); d->fLoLimit = tp[6].cast(); d->fUpLimit = tp[7].cast(); d->fLoLimValid = tp[8].cast(); d->fUpLimValid = tp[9].cast(); return p; })) ; } iminuit-2.24.0/src/print.cpp0000644000000000000000000000145314332717401012637 0ustar00#include #include namespace py = pybind11; using namespace ROOT::Minuit2; using cstr = const char*; using namespace pybind11::literals; void bind_print(py::module m) { py::class_(m, "MnPrint") .def(py::init(), "prefix"_a, "level"_a) .def("error", &MnPrint::Error) .def("warn", &MnPrint::Warn) .def("info", &MnPrint::Info) .def("debug", &MnPrint::Debug) .def_property_static( "global_level", [](py::object) { return MnPrint::GlobalLevel(); }, [](py::object, int x) { MnPrint::SetGlobalLevel(x); }) .def("show_prefix_stack", &MnPrint::ShowPrefixStack) .def("add_filter", &MnPrint::AddFilter) .def("clear_filter", &MnPrint::ClearFilter) ; } iminuit-2.24.0/src/printimpl.cpp0000644000000000000000000000044514332717401013521 0ustar00#include "Minuit2/MnPrint.h" #include using ROOT::Minuit2::MnPrint; void MnPrint::Impl(MnPrint::Verbosity level, const std::string& s) { const char* label[4] = {"E", "W", "I", "D"}; const int ilevel = static_cast(level); pybind11::print(label[ilevel], s); } iminuit-2.24.0/src/scan.cpp0000644000000000000000000000055714332717401012433 0ustar00#include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void bind_scan(py::module m) { py::class_(m, "MnScan") .def(py::init()) ; } iminuit-2.24.0/src/simplex.cpp0000644000000000000000000000104014332717401013154 0ustar00#include "fcn.hpp" #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; void bind_simplex(py::module m) { py::class_(m, "MnSimplex") .def(py::init(), py::keep_alive<1, 2>()) .def("set_print_level", [](MnSimplex& self, int lvl) { return self.Minimizer().Builder().SetPrintLevel(lvl); }) ; } iminuit-2.24.0/src/strategy.cpp0000644000000000000000000000657614332717401013360 0ustar00#include "equal.hpp" #include #include #include namespace ROOT { namespace Minuit2 { bool operator==(const MnStrategy& a, const MnStrategy& b) { return a.Strategy() == b.Strategy() && a.GradientNCycles() == b.GradientNCycles() && a.GradientStepTolerance() == b.GradientStepTolerance() && a.GradientTolerance() == b.GradientTolerance() && a.HessianNCycles() == b.HessianNCycles() && a.HessianStepTolerance() == b.HessianStepTolerance() && a.HessianG2Tolerance() == b.HessianG2Tolerance() && a.HessianGradientNCycles() == b.HessianGradientNCycles() && a.StorageLevel() == b.StorageLevel(); } } // namespace Minuit2 } // namespace ROOT namespace py = pybind11; using namespace ROOT::Minuit2; void set_strategy(MnStrategy& self, unsigned s) { switch (s) { case 0: self.SetLowStrategy(); break; case 1: self.SetMediumStrategy(); break; case 2: self.SetHighStrategy(); break; default: throw std::invalid_argument("invalid strategy"); } } void bind_strategy(py::module m) { py::class_(m, "MnStrategy") .def(py::init<>()) .def(py::init()) .def_property("strategy", &MnStrategy::Strategy, set_strategy) .def_property("gradient_ncycles", &MnStrategy::GradientNCycles, &MnStrategy::SetGradientNCycles) .def_property("gradient_step_tolerance", &MnStrategy::GradientStepTolerance, &MnStrategy::SetGradientStepTolerance) .def_property("gradient_tolerance", &MnStrategy::GradientTolerance, &MnStrategy::SetGradientTolerance) .def_property("hessian_ncycles", &MnStrategy::HessianNCycles, &MnStrategy::SetHessianNCycles) .def_property("hessian_step_tolerance", &MnStrategy::HessianStepTolerance, &MnStrategy::SetHessianStepTolerance) .def_property("hessian_g2_tolerance", &MnStrategy::HessianG2Tolerance, &MnStrategy::SetHessianG2Tolerance) .def_property("hessian_gradient_ncycles", &MnStrategy::HessianGradientNCycles, &MnStrategy::SetHessianGradientNCycles) .def_property("storage_level", &MnStrategy::StorageLevel, &MnStrategy::SetStorageLevel) .def(py::self == py::self) .def(py::pickle( [](const MnStrategy& self) { return py::make_tuple( self.Strategy(), self.GradientNCycles(), self.GradientStepTolerance(), self.GradientTolerance(), self.HessianNCycles(), self.HessianStepTolerance(), self.HessianG2Tolerance(), self.HessianGradientNCycles(), self.StorageLevel()); }, [](py::tuple tp) { MnStrategy str(tp[0].cast()); str.SetGradientNCycles(tp[1].cast()); str.SetGradientStepTolerance(tp[2].cast()); str.SetGradientTolerance(tp[3].cast()); str.SetHessianNCycles(tp[4].cast()); str.SetHessianStepTolerance(tp[5].cast()); str.SetHessianG2Tolerance(tp[6].cast()); str.SetHessianGradientNCycles(tp[7].cast()); str.SetStorageLevel(tp[8].cast()); return str; })) ; py::implicitly_convertible(); } iminuit-2.24.0/src/type_caster.hpp0000644000000000000000000000230214332717401014024 0ustar00#include #include #include #include namespace pybind11 { namespace detail { template struct type_caster> { using vec_t = std::vector; using value_conv = make_caster; using size_conv = make_caster; bool load(handle src, bool convert) { value.clear(); // TODO optimize for python objects that support buffer protocol if (isinstance(src)) { auto seq = reinterpret_borrow(src); if (hasattr(seq, "__len__")) value.reserve(static_cast(len(seq))); for (auto it : seq) { value_conv conv; if (!conv.load(it, convert)) return false; value.push_back(cast_op(std::move(conv))); } return true; } return false; } public: template static handle cast(T&& src, return_value_policy, handle) { array_t arr({static_cast(src.size())}); std::copy(src.begin(), src.end(), arr.mutable_data()); return arr.release(); } PYBIND11_TYPE_CASTER(vec_t, _("List[") + value_conv::name + _("]")); }; } // namespace detail } // namespace pybind11 iminuit-2.24.0/src/usercovariance.cpp0000644000000000000000000000246014332717401014513 0ustar00#include "equal.hpp" #include "type_caster.hpp" #include #include #include #include namespace ROOT { namespace Minuit2 { bool operator==(const MnUserCovariance& a, const MnUserCovariance& b) { return a.Nrow() == b.Nrow() && a.Data() == b.Data(); } } // namespace Minuit2 } // namespace ROOT namespace py = pybind11; using namespace ROOT::Minuit2; MnUserCovariance init(py::sequence seq, unsigned n) { return MnUserCovariance{py::cast>(seq), n}; } void bind_usercovariance(py::module m) { py::class_(m, "MnUserCovariance") .def(py::init(&init)) .def("__getitem__", [](const MnUserCovariance& self, py::object args) { const auto tup = py::cast>(args); return self(tup.first, tup.second); }) .def_property_readonly("nrow", &MnUserCovariance::Nrow) .def(py::self == py::self) .def(py::pickle( [](const MnUserCovariance& self) { return py::make_tuple(self.Data(), self.Nrow()); }, [](py::tuple tp) { return MnUserCovariance(tp[0].cast>(), tp[1].cast()); })) ; } iminuit-2.24.0/src/userparameterstate.cpp0000644000000000000000000001414114332717401015421 0ustar00#include "equal.hpp" #include "type_caster.hpp" #include #include #include #include namespace ROOT { namespace Minuit2 { bool operator==(const MnUserParameterState& a, const MnUserParameterState& b) { return a.MinuitParameters() == b.MinuitParameters() && a.Fval() == b.Fval() && a.Covariance() == b.Covariance() && a.GlobalCC().GlobalCC() == b.GlobalCC().GlobalCC() && a.IntParameters() == b.IntParameters() && a.IntCovariance().Data() == b.IntCovariance().Data() && a.CovarianceStatus() == b.CovarianceStatus() && a.IsValid() == b.IsValid() && a.HasCovariance() == b.HasCovariance() && a.HasGlobalCC() == b.HasGlobalCC() && a.Fval() == b.Fval() && a.Edm() == b.Edm() && a.NFcn() == b.NFcn(); } } // namespace Minuit2 } // namespace ROOT namespace py = pybind11; using namespace ROOT::Minuit2; int size(const MnUserParameterState& self) { return static_cast(self.MinuitParameters().size()); } const MinuitParameter& getitem(const MnUserParameterState& self, int i) { const int n = size(self); if (i < 0) i += n; if (i >= n) throw py::index_error(); return self.Parameter(i); } auto iter(const MnUserParameterState& self) { return py::make_iterator(self.MinuitParameters().begin(), self.MinuitParameters().end()); } py::object globalcc2py(const MnGlobalCorrelationCoeff& gcc) { if (gcc.IsValid()) return py::cast(gcc.GlobalCC()); return py::cast(nullptr); } MnGlobalCorrelationCoeff py2globalcc(py::object o) { static_assert(std::is_standard_layout(), ""); struct Layout { std::vector fGlobalCC; bool fValid; }; MnGlobalCorrelationCoeff c; auto d = reinterpret_cast(&c); if (!o.is_none()) { d->fGlobalCC = o.cast>(); d->fValid = true; } return c; } void bind_userparameterstate(py::module m) { py::class_(m, "MnUserParameterState") .def(py::init<>()) .def(py::init()) .def("add", py::overload_cast(&MnUserParameterState::Add)) .def("add", py::overload_cast( &MnUserParameterState::Add)) .def("add", py::overload_cast( &MnUserParameterState::Add)) .def("fix", py::overload_cast(&MnUserParameterState::Fix)) .def("release", py::overload_cast(&MnUserParameterState::Release)) .def("set_value", py::overload_cast(&MnUserParameterState::SetValue)) .def("set_error", py::overload_cast(&MnUserParameterState::SetError)) .def("set_limits", py::overload_cast( &MnUserParameterState::SetLimits)) .def("set_upper_limit", py::overload_cast(&MnUserParameterState::SetUpperLimit)) .def("set_lower_limit", py::overload_cast(&MnUserParameterState::SetLowerLimit)) .def("remove_limits", py::overload_cast(&MnUserParameterState::RemoveLimits)) .def_property_readonly("fval", &MnUserParameterState::Fval) .def_property_readonly("edm", &MnUserParameterState::Edm) .def_property_readonly("covariance", &MnUserParameterState::Covariance) .def_property_readonly( "globalcc", [](const MnUserParameterState& self) { return globalcc2py(self.GlobalCC()); }) .def_property_readonly("is_valid", &MnUserParameterState::IsValid) .def_property_readonly("has_covariance", &MnUserParameterState::HasCovariance) .def_property_readonly("trafo", &MnUserParameterState::Trafo) .def("__len__", size) .def("__getitem__", getitem) .def("__iter__", iter) .def(py::self == py::self) .def(py::pickle( [](const MnUserParameterState& self) { return py::make_tuple(self.IsValid(), self.HasCovariance(), self.HasGlobalCC(), self.CovarianceStatus(), self.Fval(), self.Edm(), self.NFcn(), self.Trafo(), self.Covariance(), globalcc2py(self.GlobalCC()), self.IntParameters(), self.IntCovariance()); }, [](py::tuple tp) { static_assert(std::is_standard_layout(), ""); static_assert(std::is_standard_layout(), ""); if (tp.size() != 12) throw std::runtime_error("invalid state"); struct Layout { bool fValid; bool fCovarianceValid; bool fGCCValid; int fCovStatus; // covariance matrix status double fFVal; double fEDM; unsigned int fNFcn; MnUserParameters fParameters; MnUserCovariance fCovariance; MnGlobalCorrelationCoeff fGlobalCC; std::vector fIntParameters; MnUserCovariance fIntCovariance; }; MnUserParameterState st; // evil workaround, will segfault or cause UB if source layout changes auto d = reinterpret_cast(&st); d->fValid = tp[0].cast(); d->fCovarianceValid = tp[1].cast(); d->fGCCValid = tp[2].cast(); d->fCovStatus = tp[3].cast(); d->fFVal = tp[4].cast(); d->fEDM = tp[5].cast(); d->fNFcn = tp[6].cast(); reinterpret_cast(d->fParameters) = tp[7].cast(); d->fCovariance = tp[8].cast(); d->fGlobalCC = py2globalcc(tp[9]); d->fIntParameters = tp[10].cast>(); d->fIntCovariance = tp[11].cast(); return st; })) ; } iminuit-2.24.0/src/usertransformation.cpp0000644000000000000000000000504214332717401015446 0ustar00#include #include #include #include namespace py = pybind11; using namespace ROOT::Minuit2; static_assert(std::is_standard_layout(), ""); struct Layout { MnMachinePrecision fPrecision; std::vector fParameters; std::vector fExtOfInt; SinParameterTransformation fDoubleLimTrafo; SqrtUpParameterTransformation fUpperLimTrafo; SqrtLowParameterTransformation fLowerLimTrafo; mutable std::vector fCache; }; int size(const MnUserTransformation& self) { return static_cast(self.Parameters().size()); } auto iter(const MnUserTransformation& self) { return py::make_iterator(self.Parameters().begin(), self.Parameters().end()); } const auto& getitem(const MnUserTransformation& self, int i) { if (i < 0) i += size(self); if (i < 0 || i >= size(self)) throw py::index_error(); return self.Parameter(i); } void bind_usertransformation(py::module m) { py::class_(m, "MnUserTransformation") .def(py::init<>()) .def("name", &MnUserTransformation::GetName) .def("index", &MnUserTransformation::FindIndex) .def("ext2int", &MnUserTransformation::Ext2int) .def("int2ext", &MnUserTransformation::Int2ext) .def("dint2ext", &MnUserTransformation::DInt2Ext) .def("ext_of_int", &MnUserTransformation::ExtOfInt) .def("int_of_ext", &MnUserTransformation::IntOfExt) .def_property_readonly("variable_parameters", &MnUserTransformation::VariableParameters) .def("__len__", size) .def("__iter__", iter) .def("__getitem__", getitem) .def(py::pickle( [](const MnUserTransformation& self) { const auto d = reinterpret_cast(&self); return py::make_tuple(self.Precision().Eps(), self.Parameters(), d->fExtOfInt, self.InitialParValues()); }, [](py::tuple tp) { if (tp.size() != 4) throw std::runtime_error("invalid state"); MnUserTransformation tr; tr.SetPrecision(tp[0].cast()); // evil workaround, will segfault or cause UB if source layout changes auto d = reinterpret_cast(&tr); d->fParameters = tp[1].cast>(); d->fExtOfInt = tp[2].cast>(); d->fCache = tp[3].cast>(); return tr; })) ; } iminuit-2.24.0/tests/fmin_approximate.txt0000644000000000000000000000347014332717401015456 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ Migrad │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = 11.46 (χ²/ndof = 1.1) │ Nfcn = 10, Ngrad = 3 │ │ EDM = 1.23e-10 (Goal: 0.0001) │ │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Valid Minimum │ Below EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ No parameters at limit │ Below call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse ok │ Covariance APPROXIMATE │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/fmin_bad.html0000644000000000000000000000211014332717401013766 0ustar00
SciPy[L-BFGS-B]
FCN = nan Nfcn = 100000, Ngrad = 200000
EDM = 1.23e-10 (Goal: 1e-05) time = 1.2 sec
INVALID Minimum ABOVE EDM threshold (goal x 10)
SOME parameters at limit ABOVE call limit
Hesse FAILED ABOVE call limit
iminuit-2.24.0/tests/fmin_bad.txt0000644000000000000000000000346614332717401013660 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ SciPy[L-BFGS-B] │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = nan │ Nfcn = 100000, Ngrad = 200000 │ │ EDM = 1.23e-10 (Goal: 1e-05) │ time = 1.2 sec │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ INVALID Minimum │ ABOVE EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ SOME parameters at limit │ ABOVE call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse FAILED │ ABOVE call limit │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/fmin_good.html0000644000000000000000000000207514332717401014202 0ustar00
Migrad
FCN = 11.46 (χ²/ndof = 1.1) Nfcn = 10, Ngrad = 3
EDM = 1.23e-10 (Goal: 0.0001)
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance accurate
iminuit-2.24.0/tests/fmin_good.txt0000644000000000000000000000347014332717401014055 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ Migrad │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = 11.46 (χ²/ndof = 1.1) │ Nfcn = 10, Ngrad = 3 │ │ EDM = 1.23e-10 (Goal: 0.0001) │ │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Valid Minimum │ Below EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ No parameters at limit │ Below call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse ok │ Covariance accurate │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/fmin_made_posdef.txt0000644000000000000000000000347014332717401015373 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ Migrad │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = 11.46 (χ²/ndof = 1.1) │ Nfcn = 10, Ngrad = 3 │ │ EDM = 1.23e-10 (Goal: 0.0001) │ │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Valid Minimum │ Below EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ No parameters at limit │ Below call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse ok │ Covariance FORCED pos. def. │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/fmin_no_cov.html0000644000000000000000000000207414332717401014534 0ustar00
Migrad
FCN = 11.46 (χ²/ndof = 1.1) Nfcn = 10, Ngrad = 3
EDM = 1.23e-10 (Goal: 0.0001)
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse not run NO covariance
iminuit-2.24.0/tests/fmin_no_cov.txt0000644000000000000000000000347014332717401014410 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ Migrad │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = 11.46 (χ²/ndof = 1.1) │ Nfcn = 10, Ngrad = 3 │ │ EDM = 1.23e-10 (Goal: 0.0001) │ │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Valid Minimum │ Below EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ No parameters at limit │ Below call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse not run │ NO covariance │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/fmin_not_posdef.txt0000644000000000000000000000347014332717401015265 0ustar00┌─────────────────────────────────────────────────────────────────────────┐ │ Migrad │ ├──────────────────────────────────┬──────────────────────────────────────┤ │ FCN = 11.46 (χ²/ndof = 1.1) │ Nfcn = 10, Ngrad = 3 │ │ EDM = 1.23e-10 (Goal: 0.0001) │ │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Valid Minimum │ Below EDM threshold (goal x 10) │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ No parameters at limit │ Below call limit │ ├──────────────────────────────────┼──────────────────────────────────────┤ │ Hesse FAILED │ Covariance NOT pos. def. │ └──────────────────────────────────┴──────────────────────────────────────┘ iminuit-2.24.0/tests/matrix.html0000644000000000000000000000057114332717401013544 0ustar00
x y
x 1 0.00
y 0.00 0.25
iminuit-2.24.0/tests/matrix.txt0000644000000000000000000000037414332717401013420 0ustar00┌───┬─────────────┐ │ │ x y │ ├───┼─────────────┤ │ x │ 1 -0.00 │ │ y │ -0.00 0.25 │ └───┴─────────────┘ iminuit-2.24.0/tests/matrix_2.html0000644000000000000000000000056414332717401013767 0ustar00
x y
x 1 0.7
y 0.7 1
iminuit-2.24.0/tests/matrix_difficult_values.txt0000644000000000000000000000037414332717401017030 0ustar00┌───┬─────────────┐ │ │ x y │ ├───┼─────────────┤ │ x │ -1.23 0.0 │ │ y │ 0.0 0 │ └───┴─────────────┘ iminuit-2.24.0/tests/matrix_large.txt0000644000000000000000000000031014332717401014560 0ustar00┌───┬───────┐ │ │ x y z │ ├───┼───────┤ │ x │ 1 2 3 │ │ y │ 4 5 6 │ │ z │ 7 8 9 │ └───┴───────┘ iminuit-2.24.0/tests/matrix_long_names.txt0000644000000000000000000000122414332717401015615 0ustar00┌─────────────────┬─────────────────────────────────┐ │ │ super-long-name x │ ├─────────────────┼─────────────────────────────────┤ │ super-long-name │ 1 0.1 │ │ x │ 0.1 1 │ └─────────────────┴─────────────────────────────────┘ iminuit-2.24.0/tests/matrix_mini.txt0000644000000000000000000000016414332717401014431 0ustar00┌───┬───┐ │ │ x │ ├───┼───┤ │ x │ 1 │ └───┴───┘ iminuit-2.24.0/tests/merror.html0000644000000000000000000000166714332717401013555 0ustar00
x
Error -1 1
Valid True True
At Limit False False
Max FCN False False
New Min False False
iminuit-2.24.0/tests/merror.txt0000644000000000000000000000112314332717401013413 0ustar00┌──────────┬───────────────────────┐ │ │ x │ ├──────────┼───────────┬───────────┤ │ Error │ -1 │ 1 │ │ Valid │ True │ True │ │ At Limit │ False │ False │ │ Max FCN │ False │ False │ │ New Min │ False │ False │ └──────────┴───────────┴───────────┘ iminuit-2.24.0/tests/merrors.html0000644000000000000000000000256614332717401013737 0ustar00
x y
Error -1 1 -0.5 0.5
Valid True True True True
At Limit False False False False
Max FCN False False False False
New Min False False False False
iminuit-2.24.0/tests/merrors.txt0000644000000000000000000000172114332717401013602 0ustar00┌──────────┬───────────────────────┬───────────────────────┐ │ │ x │ y │ ├──────────┼───────────┬───────────┼───────────┬───────────┤ │ Error │ -1 │ 1 │ -0.5 │ 0.5 │ │ Valid │ True │ True │ True │ True │ │ At Limit │ False │ False │ False │ False │ │ Max FCN │ False │ False │ False │ False │ │ New Min │ False │ False │ False │ False │ └──────────┴───────────┴───────────┴───────────┴───────────┘ iminuit-2.24.0/tests/params.html0000644000000000000000000000164414332717401013525 0ustar00
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x 2 1 -1 1
1 y 1.0 0.5 -0.5 0.5
iminuit-2.24.0/tests/params.txt0000644000000000000000000000217214332717401013375 0ustar00┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ x │ 2 │ 1 │ -1 │ 1 │ │ │ │ │ 1 │ y │ 1.0 │ 0.5 │ -0.5 │ 0.5 │ │ │ │ └───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/params_difficult_values.txt0000644000000000000000000000201314332717401016777 0ustar00┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ x │ -0 │ 0.012e-9 │ │ │ │ │ CONST │ └───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/params_init.html0000644000000000000000000000163614332717401014551 0ustar00
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x 0.0 0.1
1 y 0.0 0.1
iminuit-2.24.0/tests/params_latex_1.txt0000644000000000000000000000201614332717401015007 0ustar00┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ 𝛼 │ 1.00 │ 0.01 │ │ │ │ │ │ └───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/params_latex_2.txt0000644000000000000000000000206714332717401015016 0ustar00┌───┬──────────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼──────────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ $\alpha$ │ 1.00 │ 0.01 │ │ │ │ │ │ └───┴──────────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/params_long_names.txt0000644000000000000000000000220414332717401015573 0ustar00┌───┬─────────────────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼─────────────────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ super-long-name │ 0 │ 0 │ │ │ │ │ │ └───┴─────────────────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/params_with_limits.html0000644000000000000000000000164514332717401016142 0ustar00
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 x 3.0 0.2 0 yes
1 y 5.0 0.1 0 10
iminuit-2.24.0/tests/params_with_limits.txt0000644000000000000000000000217214332717401016011 0ustar00┌───┬──────┬───────────┬───────────┬────────────┬────────────┬─────────┬─────────┬───────┐ │ │ Name │ Value │ Hesse Err │ Minos Err- │ Minos Err+ │ Limit- │ Limit+ │ Fixed │ ├───┼──────┼───────────┼───────────┼────────────┼────────────┼─────────┼─────────┼───────┤ │ 0 │ x │ 3.0 │ 0.2 │ │ │ 0 │ │ yes │ │ 1 │ y │ 5.0 │ 0.1 │ │ │ 0 │ 10 │ │ └───┴──────┴───────────┴───────────┴────────────┴────────────┴─────────┴─────────┴───────┘ iminuit-2.24.0/tests/test_core.py0000644000000000000000000001504114332717401013711 0ustar00from iminuit._core import ( FCN, MnUserParameterState, MnUserTransformation, MnMigrad, MnStrategy, MnScan, FunctionMinimum, MnSimplex, MnPrint, MnUserCovariance, MinimumState, ) from pytest import approx import pytest import pickle import numpy as np @pytest.fixture def debug(): prev = MnPrint.global_level MnPrint.global_level = 3 MnPrint.show_prefix_stack(True) yield MnPrint.global_level = prev MnPrint.show_prefix_stack(False) def test_MnStrategy(): assert MnStrategy() == 1 assert MnStrategy(0) == 0 assert MnStrategy(2) == 2 s = MnStrategy() s.strategy = 2 assert s.strategy == 2 assert s != 1 assert not (s != 2) def test_MnUserCovariance(): c = MnUserCovariance((1, 2, 3), 2) assert c.nrow == 2 assert c[(0, 0)] == 1 assert c[(1, 0)] == 2 assert c[(0, 1)] == 2 assert c[(1, 1)] == 3 pkl = pickle.dumps(c) c2 = pickle.loads(pkl) assert c2.nrow == 2 assert c2[(0, 0)] == 1 assert c2[(1, 1)] == 3 assert c2 == c def fn(x, y): return 10 + x**2 + ((y - 1) / 2) ** 2 def fn_grad(x, y): return (2 * x, y - 1) def test_MnUserParameterState(): st = MnUserParameterState() st.add("x", 1, 0.2) st.add("😁", 3, 0.3, 1, 4) assert len(st) == 2 assert st[0].number == 0 assert st[0].name == "x" assert st[0].value == 1 assert st[0].error == 0.2 assert st[1].number == 1 assert st[1].name == "😁" assert st[1].value == 3 assert st[1].error == 0.3 assert st[1].lower_limit == 1 assert st[1].upper_limit == 4 st2 = MnUserParameterState(st) assert st2 == st st2.set_value(0, 1.1) assert st2 != st def test_MnMigrad(): fcn = FCN(fn, None, False, 1) state = MnUserParameterState() state.add("x", 5, 0.1) state.add("y", 3, 0.2, -5, 5) migrad = MnMigrad(fcn, state, 1) fmin = migrad(0, 0.1) assert fmin.is_valid state = fmin.state assert state[0].value == approx(0, abs=5e-3) assert state[0].error == approx(1, abs=5e-3) assert state[1].value == approx(1, abs=5e-3) assert state[1].error == approx(2, abs=6e-2) assert fcn._nfcn > 0 assert fcn._ngrad == 0 def test_MnMigrad_grad(): fcn = FCN(lambda x: 10 + x**2, lambda x: [2 * x], False, 1) state = MnUserParameterState() state.add("x", 5, 0.1) migrad = MnMigrad(fcn, state, 1) fmin = migrad(0, 0.1) state = fmin.state assert len(state) == 1 assert state[0].number == 0 assert state[0].name == "x" assert state[0].value == approx(0, abs=1e-3) assert state[0].error == approx(1, abs=1e-3) assert fcn._nfcn > 0 assert fcn._ngrad > 0 def test_MnMigrad_cfunc(): nb = pytest.importorskip("numba") c_sig = nb.types.double(nb.types.uintc, nb.types.CPointer(nb.types.double)) y = np.arange(5) @nb.cfunc(c_sig) def fcn(n, x): x = nb.carray(x, (n,)) r = 0.0 for i in range(n): r += (y[i] - x[i]) ** 2 return r fcn = FCN(fcn, None, True, 1) state = MnUserParameterState() for i in range(len(y)): state.add(f"x{i}", 5, 0.1) migrad = MnMigrad(fcn, state, 1) fmin = migrad(0, 0.1) state = fmin.state assert len(state) == len(y) for i, p in enumerate(state): assert p.number == i assert p.value == approx(i, abs=1e-3) assert p.error == approx(1, abs=1e-3) def test_MnMigrad_np(): fcn = FCN( lambda xy: 10 + xy[0] ** 2 + ((xy[1] - 1) / 2) ** 2, lambda xy: [2 * xy[0], (xy[1] - 1)], True, 1, ) state = MnUserParameterState() state.add("x", 5, 0.1) state.add("😁", 3, 0.2, -5, 5) assert len(state) == 2 str = MnStrategy(2) migrad = MnMigrad(fcn, state, str) fmin = migrad(0, 0.1) state = fmin.state assert len(state) == 2 assert state[0].number == 0 assert state[0].name == "x" assert state[0].value == approx(0, abs=1e-2) assert state[0].error == approx(1, abs=1e-2) assert state[1].number == 1 assert state[1].name == "😁" assert state[1].value == approx(1, abs=1e-2) assert state[1].error == approx(2, abs=6e-2) assert fcn._nfcn > 0 assert fcn._ngrad > 0 def test_MnScan(): fcn = FCN(lambda x: 10 + x**2, None, False, 1) state = MnUserParameterState() state.add("x", 2, 5) scan = MnScan(fcn, state, 1) fmin = scan(0, 0.1) assert fmin.is_valid state = fmin.state assert len(state) == 1 assert state[0].value == approx(0, abs=1e-2) def test_MnSimplex(): fcn = FCN(lambda x: 10 + x**2, None, False, 1) state = MnUserParameterState() state.add("x", 2, 5) simplex = MnSimplex(fcn, state, 1) fmin = simplex(0, 0.1) assert fmin.is_valid state = fmin.state assert len(state) == 1 assert state[0].value == approx(0, abs=5e-2) def test_FunctionMinimum(): fcn = FCN(lambda x: 10 + x**2, None, False, 1) st = MnUserParameterState() st.add("x", 0.01, 5) str = MnStrategy(1) fm1 = FunctionMinimum(fcn, st, str, 0.2) assert fm1.is_valid assert len(fm1.state) == 1 assert fm1.fval == 10.0001 fm2 = FunctionMinimum(fcn, st, str, 0) assert not fm2.is_valid def test_FunctionMinimum_pickle(): st = MnUserParameterState() st.add("x", 1, 0.1) st.add("y", 2, 0.1, 1, 3) fm = FunctionMinimum(FCN(fn, None, False, 1), st, 1, 0.1) pkl = pickle.dumps(fm) fm2 = pickle.loads(pkl) assert len(fm.state) == len(fm2.state) assert fm.state == fm2.state assert fm.edm == fm2.edm assert fm.fval == fm2.fval assert fm.is_valid == fm2.is_valid assert fm.has_accurate_covar == fm2.has_accurate_covar assert fm.has_posdef_covar == fm2.has_posdef_covar assert fm.has_made_posdef_covar == fm2.has_made_posdef_covar assert fm.hesse_failed == fm2.hesse_failed assert fm.has_covariance == fm2.has_covariance assert fm.is_above_max_edm == fm2.is_above_max_edm assert fm.has_reached_call_limit == fm2.has_reached_call_limit assert fm.errordef == fm2.errordef def test_MnUserTransformation_pickle(): tr = MnUserTransformation() pkl = pickle.dumps(tr) tr2 = pickle.loads(pkl) assert len(tr2) == len(tr) def test_MinimumState_pickle(): st = MinimumState(3) pkl = pickle.dumps(st) st2 = pickle.loads(pkl) assert st.vec == st2.vec assert st.fval == st2.fval assert st.edm == st2.edm assert st.nfcn == st2.nfcn assert st.is_valid == st2.is_valid assert st.has_parameters == st2.has_parameters assert st.has_covariance == st2.has_covariance iminuit-2.24.0/tests/test_cost.py0000644000000000000000000011747514332717401013747 0ustar00from __future__ import annotations import pytest import numpy as np from numpy.testing import assert_allclose, assert_equal from iminuit import Minuit from iminuit.cost import ( CostSum, UnbinnedNLL, BinnedNLL, ExtendedUnbinnedNLL, ExtendedBinnedNLL, LeastSquares, Constant, NormalConstraint, Template, multinominal_chi2, _soft_l1_loss, PerformanceWarning, ) from iminuit.util import describe from iminuit.typing import Annotated, Gt, Lt from typing import Sequence import pickle def norm_logpdf(x, mu, sigma): z = (x - mu) / sigma return -0.5 * (np.log(2 * np.pi) + z**2) - np.log(sigma) def norm_pdf(x, mu, sigma): return np.exp(norm_logpdf(x, mu, sigma)) def norm_cdf(x, mu, sigma): from math import erf z = (x - mu) / (np.sqrt(2) * sigma) return (1 + np.vectorize(erf)(z)) * 0.5 def mvnorm(mux, muy, sx, sy, rho): stats = pytest.importorskip("scipy.stats") C = np.empty((2, 2)) C[0, 0] = sx**2 C[0, 1] = C[1, 0] = sx * sy * rho C[1, 1] = sy**2 m = [mux, muy] return stats.multivariate_normal(m, C) def expon_cdf(x, a): with np.errstate(over="ignore"): return 1 - np.exp(-x / a) @pytest.fixture def unbinned(): rng = np.random.default_rng(1) x = rng.normal(size=1000) mle = (len(x), np.mean(x), np.std(x, ddof=1)) return mle, x @pytest.fixture def binned(unbinned): mle, x = unbinned nx, xe = np.histogram(x, bins=50, range=(-3, 3)) assert np.sum(nx == 0) > 0 return mle, nx, xe def logpdf(x, mu, sigma): return norm_logpdf(x, mu, sigma) def pdf(x, mu, sigma): return norm_pdf(x, mu, sigma) def cdf(x, mu, sigma): return norm_cdf(x, mu, sigma) def scaled_cdf(x, n, mu, sigma): return n * norm_cdf(x, mu, sigma) def line(x, a, b): return a + b * x def test_norm_logpdf(): norm = pytest.importorskip("scipy.stats").norm x = np.linspace(-3, 3) assert_allclose(norm_logpdf(x, 3, 2), norm.logpdf(x, 3, 2)) def test_norm_pdf(): norm = pytest.importorskip("scipy.stats").norm x = np.linspace(-3, 3) assert_allclose(norm_pdf(x, 3, 2), norm.pdf(x, 3, 2)) def test_norm_cdf(): norm = pytest.importorskip("scipy.stats").norm x = np.linspace(-3, 3) assert_allclose(norm_cdf(x, 3, 2), norm.cdf(x, 3, 2)) def test_describe(): c = Constant(2.5) assert describe(c, annotations=True) == {} c = NormalConstraint("foo", 1.5, 0.1) assert describe(c, annotations=True) == {"foo": None} def model( x, foo: Annotated[float, Gt(1), Lt(2)], bar: float, baz: Annotated[float, 0:] ): return x assert describe(model, annotations=True) == { "x": None, "foo": (1, 2), "bar": None, "baz": (0, np.inf), } c = UnbinnedNLL([], model) assert describe(c, annotations=True) == { "foo": (1, 2), "bar": None, "baz": (0, np.inf), } c = BinnedNLL([], [1], model) assert describe(c, annotations=True) == { "foo": (1, 2), "bar": None, "baz": (0, np.inf), } def test_Constant(): c = Constant(2.5) assert c.value == 2.5 assert c.ndata == 0 @pytest.mark.parametrize("verbose", (0, 1)) @pytest.mark.parametrize("model", (logpdf, pdf)) def test_UnbinnedNLL(unbinned, verbose, model): mle, x = unbinned cost = UnbinnedNLL(x, model, verbose=verbose, log=model is logpdf) assert cost.ndata == np.inf m = Minuit(cost, mu=0, sigma=1) m.limits["sigma"] = (0, None) m.migrad() assert_allclose(m.values, mle[1:], atol=1e-3) assert m.errors["mu"] == pytest.approx(1000**-0.5, rel=0.05) assert_equal(m.fmin.reduced_chi2, np.nan) def test_UnbinnedNLL_2D(): def model(x_y, mux, muy, sx, sy, rho): return mvnorm(mux, muy, sx, sy, rho).pdf(x_y.T) truth = 0.1, 0.2, 0.3, 0.4, 0.5 x, y = mvnorm(*truth).rvs(size=1000, random_state=1).T cost = UnbinnedNLL((x, y), model) m = Minuit(cost, *truth) m.limits["sx", "sy"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.02) def test_UnbinnedNLL_mask(): c = UnbinnedNLL([1, np.nan, 2], lambda x, a: x + a) assert c.mask is None assert np.isnan(c(0)) c.mask = np.arange(3) != 1 assert_equal(c.mask, (True, False, True)) assert not np.isnan(c(0)) @pytest.mark.parametrize("log", (False, True)) def test_UnbinnedNLL_properties(log): c = UnbinnedNLL([1, 2], norm_logpdf if log else norm_pdf, log=log) x = np.linspace(0, 1) expected = norm_pdf(x, 1, 2) assert_allclose(c.pdf(x, 1, 2), expected) expected *= len(c.data) assert_allclose(c.scaled_pdf(x, 1, 2), expected) with pytest.raises(AttributeError): c.pdf = None with pytest.raises(AttributeError): c.scaled_pdf = None assert_equal(c.data, [1, 2]) c.data = [2, 3] assert_equal(c.data, [2, 3]) with pytest.raises(ValueError): c.data = [1, 2, 3] assert c.verbose == 0 c.verbose = 1 assert c.verbose == 1 @pytest.mark.parametrize("log", (False, True)) def test_UnbinnedNLL_visualize(log): pytest.importorskip("matplotlib") c = UnbinnedNLL([1, 2], norm_logpdf if log else norm_pdf, log=log) # auto-sampling c.visualize((1, 2)) # linear spacing c.visualize((1, 2), model_points=10) # linear spacing and different binning c.visualize((1, 2), model_points=10, bins=20) # trigger log-spacing c = UnbinnedNLL([1, 100, 1000], norm_logpdf if log else norm_pdf, log=log) c.visualize((1, 2), model_points=10) c.visualize((1, 2), model_points=10, bins=20) # manual spacing c.visualize((1, 2), model_points=np.linspace(1, 1000)) with pytest.warns( np.VisibleDeprecationWarning, match="keyword 'nbins' is deprecated" ): c.visualize((1, 2), nbins=20) def test_UnbinnedNLL_visualize_2D(): pytest.importorskip("matplotlib") def model(x_y, mux, muy, sx, sy, rho): return mvnorm(mux, muy, sx, sy, rho).pdf(x_y.T) truth = 0.1, 0.2, 0.3, 0.4, 0.5 x, y = mvnorm(*truth).rvs(size=10, random_state=1).T c = UnbinnedNLL((x, y), model) with pytest.raises(ValueError, match="not implemented for multi-dimensional"): c.visualize(truth) def test_UnbinnedNLL_pickle(): c = UnbinnedNLL([1, 2], norm_pdf) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) def test_UnbinnedNLL_annotated(): def model( x: np.ndarray, mu: float, sigma: Annotated[float, 0 : np.inf], ) -> np.ndarray: return norm_pdf(x, mu, sigma) c = UnbinnedNLL([], model) m = Minuit(c, mu=1, sigma=1.5) assert m.limits[0] == (-np.inf, np.inf) assert m.limits[1] == (0, np.inf) @pytest.mark.parametrize("verbose", (0, 1)) @pytest.mark.parametrize("model", (logpdf, pdf)) def test_ExtendedUnbinnedNLL(unbinned, verbose, model): mle, x = unbinned log = model is logpdf def density(x, n, mu, sigma): if log: return n, np.log(n) + logpdf(x, mu, sigma) return n, n * pdf(x, mu, sigma) cost = ExtendedUnbinnedNLL(x, density, verbose=verbose, log=log) assert cost.ndata == np.inf m = Minuit(cost, n=len(x), mu=0, sigma=1) m.limits["n"] = (0, None) m.limits["sigma"] = (0, None) m.migrad() assert_allclose(m.values, mle, atol=1e-3) assert m.errors["mu"] == pytest.approx(1000**-0.5, rel=0.05) assert_equal(m.fmin.reduced_chi2, np.nan) def test_ExtendedUnbinnedNLL_2D(): def model(x_y, n, mux, muy, sx, sy, rho): return n * 1000, n * 1000 * mvnorm(mux, muy, sx, sy, rho).pdf(x_y.T) truth = 1.0, 0.1, 0.2, 0.3, 0.4, 0.5 x, y = mvnorm(*truth[1:]).rvs(size=int(truth[0] * 1000)).T cost = ExtendedUnbinnedNLL((x, y), model) m = Minuit(cost, *truth) m.limits["n", "sx", "sy"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.1) def test_ExtendedUnbinnedNLL_mask(): c = ExtendedUnbinnedNLL([1, np.nan, 2], lambda x, a: (1, x + a)) assert c.ndata == np.inf assert np.isnan(c(0)) c.mask = np.arange(3) != 1 assert not np.isnan(c(0)) assert c.ndata == np.inf @pytest.mark.parametrize("log", (False, True)) def test_ExtendedUnbinnedNLL_properties(log): def log_model(x, s, mu, sigma): return s, np.log(s) + norm_logpdf(x, mu, sigma) def model(x, s, mu, sigma): n, y = log_model(x, s, mu, sigma) return n, np.exp(y) c = ExtendedUnbinnedNLL([1, 2, 3], log_model if log else model, log=log) x = np.linspace(0, 1) scale, expected = model(x, 1, 2, 3) assert_allclose(c.scaled_pdf(x, 1, 2, 3), expected) expected /= scale assert_allclose(c.pdf(x, 1, 2, 3), expected) with pytest.raises(AttributeError): c.scaled_pdf = None with pytest.raises(AttributeError): c.pdf = None @pytest.mark.parametrize("log", (False, True)) def test_ExtendedUnbinnedNLL_visualize(log): pytest.importorskip("matplotlib") def log_model(x, s, mu, sigma): return s, np.log(s) + norm_logpdf(x, mu, sigma) def model(x, s, mu, sigma): return s, s * norm_pdf(x, mu, sigma) c = ExtendedUnbinnedNLL( [1, 2, 3], log_model if log else model, log=log, ) c.visualize((1, 2, 3)) def test_ExtendedUnbinnedNLL_visualize_2D(): pytest.importorskip("matplotlib") def model(x_y, n, mux, muy, sx, sy, rho): return n * 100, n * 100 * mvnorm(mux, muy, sx, sy, rho).pdf(x_y.T) truth = 1.0, 0.1, 0.2, 0.3, 0.4, 0.5 x, y = mvnorm(*truth[1:]).rvs(size=int(truth[0] * 100)).T c = ExtendedUnbinnedNLL((x, y), model) with pytest.raises(ValueError, match="not implemented for multi-dimensional"): c.visualize(truth) def test_ExtendedUnbinnedNLL_pickle(): c = ExtendedUnbinnedNLL([1, 2], norm_pdf) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) @pytest.mark.parametrize("verbose", (0, 1)) def test_BinnedNLL(binned, verbose): mle, nx, xe = binned cost = BinnedNLL(nx, xe, cdf, verbose=verbose) assert cost.ndata == len(nx) m = Minuit(cost, mu=0, sigma=1) m.limits["sigma"] = (0, None) m.migrad() # binning loses information compared to unbinned case assert_allclose(m.values, mle[1:], rtol=0.15) assert m.errors["mu"] == pytest.approx(1000**-0.5, rel=0.05) assert m.ndof == len(nx) - 2 assert_allclose(m.fmin.reduced_chi2, 1, atol=0.15) def test_BinnedNLL_pulls(binned): mle, nx, xe = binned cost = BinnedNLL(nx, xe, cdf) m = Minuit(cost, mu=0, sigma=1) m.limits["sigma"] = (0, None) m.migrad() pulls = cost.pulls(m.values) assert np.nanmean(pulls) == pytest.approx(0, abs=0.05) assert np.nanvar(pulls) == pytest.approx(1, abs=0.2) def test_BinnedNLL_weighted(): xe = np.array([0, 0.2, 0.4, 0.8, 1.5, 10]) p = np.diff(expon_cdf(xe, 1)) n = p * 1000 c = BinnedNLL(n, xe, expon_cdf) assert_equal(c.data, n) m1 = Minuit(c, 1) m1.migrad() assert m1.values[0] == pytest.approx(1, rel=1e-2) # variance * 4 = (sigma * 2)^2, constructed so that # fitted parameter has twice the uncertainty w = np.transpose((n, 4 * n)) c = BinnedNLL(w, xe, expon_cdf) assert_equal(c.data, w) m2 = Minuit(c, 1) m2.migrad() assert m2.values[0] == pytest.approx(1, rel=1e-2) assert m2.errors[0] == pytest.approx(2 * m1.errors[0], rel=1e-2) def test_ExtendedBinnedNLL_weighted_pulls(): rng = np.random.default_rng(1) xe = np.linspace(0, 5, 500) p = np.diff(expon_cdf(xe, 2)) m = p * 100000 n = rng.poisson(m * 4) / 4 nvar = m * 4 / 4**2 w = np.transpose((n, nvar)) c = ExtendedBinnedNLL(w, xe, lambda x, n, s: n * expon_cdf(x, s)) m = Minuit(c, np.sum(n), 2) m.limits["n"] = (0.1, None) m.limits["s"] = (0.1, None) m.migrad() assert m.valid pulls = c.pulls(m.values) assert np.nanmean(pulls) == pytest.approx(0, abs=0.1) assert np.nanvar(pulls) == pytest.approx(1, abs=0.2) def test_BinnedNLL_bad_input_1(): with pytest.raises(ValueError): BinnedNLL([1], [1], lambda x, a: 0) def test_BinnedNLL_bad_input_2(): with pytest.raises(ValueError): BinnedNLL([[[1]]], [1], lambda x, a: 0) def test_BinnedNLL_bad_input_3(): with pytest.raises(ValueError): BinnedNLL([[1, 2, 3]], [1], lambda x, a: 0) def test_BinnedNLL_bad_input_4(): with pytest.raises(ValueError, match="n must have shape"): BinnedNLL([[1, 2, 3]], [1, 2], lambda x, a: 0) def test_BinnedNLL_ndof_zero(): c = BinnedNLL([1], [0, 1], lambda x, scale: expon_cdf(x, scale)) m = Minuit(c, scale=1) m.migrad() assert c.ndata == m.nfit assert np.isnan(m.fmin.reduced_chi2) def test_BinnedNLL_bad_input_5(): with pytest.raises(ValueError): BinnedNLL([[1, 2, 3]], [[1, 2], [1, 2, 3]], lambda x, a: 0) def test_BinnedNLL_bad_input_6(): with pytest.raises(ValueError): BinnedNLL(1, 2, lambda x, a: 0) def test_BinnedNLL_2D(): truth = (0.1, 0.2, 0.3, 0.4, 0.5) x, y = mvnorm(*truth).rvs(size=1000, random_state=1).T w, xe, ye = np.histogram2d(x, y, bins=(20, 50)) def model(xy, mux, muy, sx, sy, rho): return mvnorm(mux, muy, sx, sy, rho).cdf(xy.T) cost = BinnedNLL(w, (xe, ye), model) assert cost.ndata == np.prod(w.shape) m = Minuit(cost, *truth) m.limits["sx", "sy"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.05) assert cost.ndata == np.prod(w.shape) w2 = w.copy() w2[1, 1] += 1 cost.n = w2 assert cost(*m.values) > m.fval def test_BinnedNLL_2D_with_zero_bins(): truth = (0.1, 0.2, 0.3, 0.4, 0.5) x, y = mvnorm(*truth).rvs(size=1000, random_state=1).T w, xe, ye = np.histogram2d(x, y, bins=(50, 100), range=((-5, 5), (-5, 5))) assert np.mean(w == 0) > 0.25 def model(xy, mux, muy, sx, sy, rho): return mvnorm(mux, muy, sx, sy, rho).cdf(xy.T) cost = BinnedNLL(w, (xe, ye), model) m = Minuit(cost, *truth) m.limits["sx", "sy"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.05) def test_BinnedNLL_mask(): c = BinnedNLL([5, 1000, 1], [0, 1, 2, 3], expon_cdf) assert c.ndata == 3 c_unmasked = c(1) c.mask = np.arange(3) != 1 assert c(1) < c_unmasked assert c.ndata == 2 def test_BinnedNLL_properties(): def cdf(x, a, b): return 0 c = BinnedNLL([1], [1, 2], cdf) assert c.cdf is cdf with pytest.raises(AttributeError): c.cdf = None assert_equal(c.n, [1]) assert_equal(c.xe, [1, 2]) c.n = [2] assert_equal(c.n, [2]) with pytest.raises(ValueError): c.n = [1, 2] def test_BinnedNLL_visualize(): pytest.importorskip("matplotlib") c = BinnedNLL([1, 2], [1, 2, 3], expon_cdf) c.visualize((1,)) c.mask = np.array([False, True]) c.visualize((1,)) def test_BinnedNLL_visualize_2D(): pytest.importorskip("matplotlib") truth = (0.1, 0.2, 0.3, 0.4, 0.5) x, y = mvnorm(*truth).rvs(size=10, random_state=1).T w, xe, ye = np.histogram2d(x, y, bins=(50, 100), range=((-5, 5), (-5, 5))) def model(xy, mux, muy, sx, sy, rho): return mvnorm(mux, muy, sx, sy, rho).cdf(xy.T) c = BinnedNLL(w, (xe, ye), model) c.visualize(truth) def test_BinnedNLL_pickle(): c = BinnedNLL([1], [1, 2], expon_cdf) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) @pytest.mark.parametrize("verbose", (0, 1)) def test_ExtendedBinnedNLL(binned, verbose): mle, nx, xe = binned cost = ExtendedBinnedNLL(nx, xe, scaled_cdf, verbose=verbose) assert cost.ndata == len(nx) m = Minuit(cost, n=mle[0], mu=0, sigma=1) m.limits["n"] = (0, None) m.limits["sigma"] = (0, None) m.migrad() # binning loses information compared to unbinned case assert_allclose(m.values, mle, rtol=0.15) assert m.errors["mu"] == pytest.approx(1000**-0.5, rel=0.05) assert m.ndof == len(nx) - 3 assert_allclose(m.fmin.reduced_chi2, 1, 0.1) def test_ExtendedBinnedNLL_weighted(): xe = np.array([0, 1, 10]) n = np.diff(expon_cdf(xe, 1)) m1 = Minuit(ExtendedBinnedNLL(n, xe, expon_cdf), 1) m1.migrad() assert_allclose(m1.values, (1,), rtol=1e-2) w = np.transpose((n, 4 * n)) m2 = Minuit(ExtendedBinnedNLL(w, xe, expon_cdf), 1) m2.migrad() assert_allclose(m2.values, (1,), rtol=1e-2) assert m2.errors[0] == pytest.approx(2 * m1.errors[0], rel=1e-2) def test_ExtendedBinnedNLL_bad_input(): with pytest.raises(ValueError): ExtendedBinnedNLL([1], [1], lambda x, a: 0) def test_ExtendedBinnedNLL_2D(): truth = (1.0, 0.1, 0.2, 0.3, 0.4, 0.5) x, y = mvnorm(*truth[1:]).rvs(size=int(truth[0] * 1000), random_state=1).T w, xe, ye = np.histogram2d(x, y, bins=(10, 20)) def model(xy, n, mux, muy, sx, sy, rho): return n * 1000 * mvnorm(mux, muy, sx, sy, rho).cdf(np.transpose(xy)) cost = ExtendedBinnedNLL(w, (xe, ye), model) assert cost.ndata == np.prod(w.shape) m = Minuit(cost, *truth) m.limits["n", "sx", "sy"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.1) def test_ExtendedBinnedNLL_3D(): norm = pytest.importorskip("scipy.stats").norm truth = (1.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7) n = int(truth[0] * 10000) x, y = mvnorm(*truth[1:-2]).rvs(size=n).T z = norm(truth[-2], truth[-1]).rvs(size=n) w, edges = np.histogramdd((x, y, z), bins=(5, 10, 20)) def model(xyz, n, mux, muy, sx, sy, rho, muz, sz): *xy, z = xyz return ( n * 10000 * mvnorm(mux, muy, sx, sy, rho).cdf(np.transpose(xy)) * norm(muz, sz).cdf(z) ) cost = ExtendedBinnedNLL(w, edges, model) assert cost.ndata == np.prod(w.shape) m = Minuit(cost, *truth) m.limits["n", "sx", "sy", "sz"] = (0, None) m.limits["rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth, atol=0.05) def test_ExtendedBinnedNLL_mask(): c = ExtendedBinnedNLL([1, 1000, 2], [0, 1, 2, 3], expon_cdf) assert c.ndata == 3 c_unmasked = c(2) c.mask = np.arange(3) != 1 assert c(2) < c_unmasked assert c.ndata == 2 def test_ExtendedBinnedNLL_properties(): def cdf(x, a): return 0 c = ExtendedBinnedNLL([1], [1, 2], cdf) assert c.scaled_cdf is cdf def test_ExtendedBinnedNLL_visualize(): pytest.importorskip("matplotlib") def model(x, s, slope): return s * expon_cdf(x, slope) c = ExtendedBinnedNLL([1], [1, 2], model) c.visualize((1, 2)) def test_ExtendedBinnedNLL_visualize_2D(): pytest.importorskip("matplotlib") truth = (1.0, 0.1, 0.2, 0.3, 0.4, 0.5) x, y = mvnorm(*truth[1:]).rvs(size=int(truth[0] * 1000), random_state=1).T w, xe, ye = np.histogram2d(x, y, bins=(10, 20)) def model(xy, n, mux, muy, sx, sy, rho): return n * 1000 * mvnorm(mux, muy, sx, sy, rho).cdf(np.transpose(xy)) c = ExtendedBinnedNLL(w, (xe, ye), model) c.visualize(truth) def test_ExtendedBinnedNLL_pickle(): c = BinnedNLL([1], [1, 2], expon_cdf) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) @pytest.mark.parametrize("loss", ["linear", "soft_l1", np.arctan]) @pytest.mark.parametrize("verbose", (0, 1)) def test_LeastSquares(loss, verbose): rng = np.random.default_rng(1) x = np.linspace(0, 1, 1000) ye = 0.1 y = rng.normal(2 * x + 1, ye) def model(x, a, b): return a + b * x cost = LeastSquares(x, y, ye, model, loss=loss, verbose=verbose) assert cost.ndata == len(x) m = Minuit(cost, a=0, b=0) m.migrad() assert_allclose(m.values, (1, 2), rtol=0.05) assert cost.loss == loss if loss != "linear": cost.loss = "linear" assert cost.loss != loss m.migrad() assert_allclose(m.values, (1, 2), rtol=0.05) assert m.ndof == len(x) - 2 assert_allclose(m.fmin.reduced_chi2, 1, atol=5e-2) def test_LeastSquares_2D(): x = np.array([1.0, 2.0, 3.0]) y = np.array([4.0, 5.0, 6.0]) z = 1.5 * x + 0.2 * y ze = 1.5 def model(xy, a, b): x, y = xy return a * x + b * y c = LeastSquares((x, y), z, ze, model) assert_equal(c.x, (x, y)) assert_equal(c.y, z) assert_equal(c.yerror, ze) assert_allclose(c(1.5, 0.2), 0.0) assert_allclose(c(2.5, 0.2), np.sum(((z - 2.5 * x - 0.2 * y) / ze) ** 2)) assert_allclose(c(1.5, 1.2), np.sum(((z - 1.5 * x - 1.2 * y) / ze) ** 2)) c.y = 2 * z assert_equal(c.y, 2 * z) c.x = (y, x) assert_equal(c.x, (y, x)) def test_LeastSquares_bad_input(): with pytest.raises(ValueError, match="shape mismatch"): LeastSquares([1, 2], [], [1], lambda x, a: 0) with pytest.raises(ValueError, match="shape mismatch"): LeastSquares([1, 2], [3, 4, 5], [1], lambda x, a: 0) with pytest.raises(ValueError, match="unknown loss"): LeastSquares([1], [1], [1], lambda x, a: 0, loss="foo") with pytest.raises(ValueError, match="loss must be str or LossFunction"): LeastSquares([1], [1], [1], lambda x, a: 0, loss=[1, 2, 3]) def test_LeastSquares_mask(): c = LeastSquares([1, 2, 3], [3, np.nan, 4], [1, 1, 1], lambda x, a: x + a) assert c.ndata == 3 assert np.isnan(c(0)) m = Minuit(c, 1) assert m.ndof == 2 m.migrad() assert not m.valid c.mask = np.arange(3) != 1 assert not np.isnan(c(0)) assert c.ndata == 2 assert m.ndof == 1 m.migrad() assert m.valid assert_equal(m.values, [1.5]) def test_LeastSquares_mask_2(): c = LeastSquares([1, 2], [1, 5], 1, lambda x, a: a * x) assert c(2) == pytest.approx(2) c.mask = [False, True] assert c(2) == pytest.approx(1) c.x = [2, 2] c.y = [4, 4] c.yerror = [2, 2] assert c(2) == pytest.approx(0) assert c(1) == pytest.approx(1) def test_LeastSquares_properties(): def model(x, a): return a c = LeastSquares(1, 2, 3, model) assert_equal(c.x, [1]) assert_equal(c.y, [2]) assert_equal(c.yerror, [3]) assert c.model is model with pytest.raises(AttributeError): c.model = model with pytest.raises(ValueError): c.x = [1, 2] with pytest.raises(ValueError): c.y = [1, 2] with pytest.raises(ValueError): c.yerror = [1, 2] def test_LeastSquares_visualize(): pytest.importorskip("matplotlib") c = LeastSquares([1, 2], [2, 3], 0.1, line) # auto-sampling c.visualize((1, 2)) # linear spacing c.visualize((1, 2), model_points=10) # trigger use of log-spacing c = LeastSquares([1, 10, 100], [2, 3, 4], 0.1, line) c.visualize((1, 2), model_points=10) # manual spacing c.visualize((1, 2), model_points=np.linspace(1, 100)) def test_LeastSquares_visualize_2D(): pytest.importorskip("matplotlib") c = LeastSquares([[1, 2]], [[2, 3]], 0.1, line) with pytest.raises(ValueError, match="not implemented for multi-dimensional"): c.visualize((1, 2)) def test_LeastSquares_pickle(): c = LeastSquares([1, 2], [2, 3], 0.1, line) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) assert c.model is c2.model def test_LeastSquares_pulls(): c = LeastSquares([1, 2], [2, 3], 0.1, line) # pulls are zero for parameters 1, 1 assert_equal(c.pulls((1, 1)), [0, 0]) # correct slope but line offset by 1 with error 0.1 produces pulls [10, 10] assert_equal(c.pulls((0, 1)), [10, 10]) c.mask = [True, False] assert_equal(c.pulls((0, 1)), [10, np.nan]) def test_CostSum_1(): def model1(x, a): return a + x def model2(x, b, a): return a + b * x def model3(x, c): return c lsq1 = LeastSquares(1, 2, 3, model1) assert describe(lsq1) == ["a"] lsq2 = LeastSquares(1, 3, 4, model2) assert describe(lsq2) == ["b", "a"] lsq3 = LeastSquares(1, 1, 1, model3) assert describe(lsq3) == ["c"] lsq12 = lsq1 + lsq2 assert lsq12._items == [lsq1, lsq2] assert isinstance(lsq12, CostSum) assert isinstance(lsq1, LeastSquares) assert isinstance(lsq2, LeastSquares) assert describe(lsq12) == ["a", "b"] assert lsq12.ndata == 2 assert lsq12(1, 2) == lsq1(1) + lsq2(2, 1) m = Minuit(lsq12, a=0, b=0) m.migrad() assert m.parameters == ("a", "b") assert_allclose(m.values, (1, 2)) assert_allclose(m.errors, (3, 5)) assert_allclose(m.covariance, ((9, -9), (-9, 25)), atol=1e-10) lsq121 = lsq12 + lsq1 assert lsq121._items == [lsq1, lsq2, lsq1] assert describe(lsq121) == ["a", "b"] assert lsq121.ndata == 3 lsq312 = lsq3 + lsq12 assert lsq312._items == [lsq3, lsq1, lsq2] assert describe(lsq312) == ["c", "a", "b"] assert lsq312.ndata == 3 lsq31212 = lsq312 + lsq12 assert lsq31212._items == [lsq3, lsq1, lsq2, lsq1, lsq2] assert describe(lsq31212) == ["c", "a", "b"] assert lsq31212.ndata == 5 lsq31212 += lsq1 assert lsq31212._items == [lsq3, lsq1, lsq2, lsq1, lsq2, lsq1] assert describe(lsq31212) == ["c", "a", "b"] assert lsq31212.ndata == 6 def test_CostSum_2(): ref = NormalConstraint("a", 1, 2), NormalConstraint(("b", "a"), (1, 1), (2, 2)) cs = ref[0] + ref[1] assert cs.ndata == 3 assert isinstance(cs, Sequence) assert len(cs) == 2 assert cs[0] is ref[0] assert cs[1] is ref[1] for c, r in zip(cs, ref): assert c is r assert cs.index(ref[0]) == 0 assert cs.index(ref[1]) == 1 assert cs.count(ref[0]) == 1 def test_CostSum_3(): def line_np(x, par): return par[0] + par[1] * x lsq = LeastSquares([1, 2, 3], [3, 4, 5], 1, line_np) con = NormalConstraint("par", (1, 1), (1, 1)) cs = sum([lsq, con]) assert cs((1, 1)) == lsq((1, 1)) + con((1, 1)) cs = 1.5 + lsq + con assert cs((1, 1)) == lsq((1, 1)) + con((1, 1)) + 1.5 def test_CostSum_4(): pytest.importorskip("scipy.special") t = Template([1, 2], [1, 2, 3], [[1, 1], [0, 1]], method="asy") assert t.errordef == Minuit.LIKELIHOOD m1 = Minuit(t, 1, 1) m1.migrad() cs = CostSum(t) assert cs.errordef == Minuit.LEAST_SQUARES m2 = Minuit(cs, 1, 1) m2.migrad() assert_allclose(m1.errors, m2.errors) def test_CostSum_visualize(): pytest.importorskip("matplotlib") lsq = LeastSquares([1, 2, 3], [3, 4, 5], 1, line) con = NormalConstraint(("a", "b"), (1, 1), (1, 1)) c = lsq + con + 1 c.visualize((1, 2)) def test_NormalConstraint_1(): def model(x, a): return a * np.ones_like(x) lsq1 = LeastSquares(0, 1, 1, model) lsq2 = lsq1 + NormalConstraint("a", 1, 0.1) assert describe(lsq1) == ["a"] assert describe(lsq2) == ["a"] assert lsq1.ndata == 1 assert lsq2.ndata == 2 m = Minuit(lsq1, 0) m.migrad() assert_allclose(m.values, (1,), atol=1e-2) assert_allclose(m.errors, (1,), rtol=1e-2) m = Minuit(lsq2, 0) m.migrad() assert_allclose(m.values, (1,), atol=1e-2) assert_allclose(m.errors, (0.1,), rtol=1e-2) def test_NormalConstraint_2(): lsq1 = NormalConstraint(("a", "b"), (1, 2), (2, 2)) lsq2 = lsq1 + NormalConstraint("b", 2, 0.1) + NormalConstraint("a", 1, 0.01) sa = 0.1 sb = 0.02 rho = 0.5 cov = ((sa**2, rho * sa * sb), (rho * sa * sb, sb**2)) lsq3 = lsq1 + NormalConstraint(("a", "b"), (1, 2), cov) assert describe(lsq1) == ["a", "b"] assert describe(lsq2) == ["a", "b"] assert describe(lsq3) == ["a", "b"] assert lsq1.ndata == 2 assert lsq2.ndata == 4 m = Minuit(lsq1, 0, 0) m.migrad() assert_allclose(m.values, (1, 2), atol=1e-3) assert_allclose(m.errors, (2, 2), rtol=1e-3) m = Minuit(lsq2, 0, 0) m.migrad() assert_allclose(m.values, (1, 2), atol=1e-3) assert_allclose(m.errors, (0.01, 0.1), rtol=1e-2) m = Minuit(lsq3, 0, 0) m.migrad() assert_allclose(m.values, (1, 2), atol=1e-3) assert_allclose(m.errors, (sa, sb), rtol=1e-2) assert_allclose(m.covariance, cov, rtol=1e-2) def test_NormalConstraint_properties(): nc = NormalConstraint(("a", "b"), (1, 2), (3, 4)) assert_equal(nc.value, (1, 2)) assert_equal(nc.covariance, (9, 16)) nc.value = (2, 3) nc.covariance = (1, 2) assert_equal(nc.value, (2, 3)) assert_equal(nc.covariance, (1, 2)) def test_NormalConstraint_visualize(): pytest.importorskip("matplotlib") c = NormalConstraint(("a", "b"), (1, 2), (3, 4)) c.visualize((1, 2)) c = NormalConstraint(("a", "b"), (1, 2), np.eye(2)) c.visualize((1, 2)) def test_NormalConstraint_pickle(): c = NormalConstraint(("a", "b"), (1, 2), (3, 4)) b = pickle.dumps(c) c2 = pickle.loads(b) assert describe(c) == describe(c2) and describe(c) == ["a", "b"] assert_equal(c.value, c2.value) assert_equal(c.covariance, c2.covariance) dtypes_to_test = [np.float32] if hasattr(np, "float128"): # not available on all platforms dtypes_to_test.append(np.float128) @pytest.mark.parametrize("dtype", dtypes_to_test) def test_soft_l1_loss(dtype): v = np.array([0], dtype=dtype) assert _soft_l1_loss(v) == v v[:] = 0.1 assert _soft_l1_loss(v) == pytest.approx(0.1, abs=0.01) v[:] = 1e10 assert _soft_l1_loss(v) == pytest.approx(2e5, rel=0.01) def test_multinominal_chi2(): zero = np.array(0) one = np.array(1) assert multinominal_chi2(zero, zero) == 0 assert multinominal_chi2(zero, one) == 0 assert multinominal_chi2(one, zero) == pytest.approx(1416, abs=1) n = np.array([(0.0, 0.0)]) assert_allclose(multinominal_chi2(n, zero), 0) assert_allclose(multinominal_chi2(n, one), 0) @pytest.mark.skipif( not hasattr(np, "float128"), reason="float128 not available on all platforms" ) def test_model_float128(): def model(x, a): x = x.astype(np.float128) return a + x for cost in ( UnbinnedNLL([1], model), ExtendedUnbinnedNLL([1], lambda x, a: (1, model(x, a))), BinnedNLL([1], [1, 2], model), BinnedNLL([[1, 1]], [1, 2], model), ExtendedBinnedNLL([1], [1, 2], model), ExtendedBinnedNLL([[1, 1]], [1, 2], model), LeastSquares([1.0], [2.0], [3.0], model), LeastSquares([1.0], [2.0], [3.0], model, loss="soft_l1"), ): assert cost(1).dtype == np.float128 Minuit(cost, a=0).migrad() # should not raise def test_model_performance_warning(): def model(x, a): return [0 for xi in x] with pytest.warns(PerformanceWarning): BinnedNLL([1], [1.0, 2.0], model)(1) with pytest.warns(PerformanceWarning): ExtendedBinnedNLL([1], [1.0, 2.0], model)(1) with pytest.warns(PerformanceWarning): UnbinnedNLL([1], model)(1) with pytest.warns(PerformanceWarning): ExtendedUnbinnedNLL([1], lambda x, a: (1, model(x, a)))(1) @pytest.mark.parametrize("cls", (BinnedNLL, ExtendedBinnedNLL)) def test_update_data_with_mask(cls): xe = np.arange(0, 4) nx = np.diff(expon_cdf(xe, 1)) nx[0] += 1 c = cls(nx.copy(), xe, expon_cdf) c.mask = [False, True, True] assert c(1) == 0 nx[0] += 1 c.n = nx assert c(1) == 0 nx[1] += 1 c.n = nx assert c(1) != 0 nx[0] -= 2 c.mask = (True, False, True) c.n = nx assert c(1) == 0 @pytest.mark.parametrize("method", ("jsc", "asy", "da")) def test_Template(method): if method == "asy": pytest.importorskip("scipy.special") xe = np.array([0, 1, 2, 3]) t = np.array([[1, 1, 0], [0, 1, 3]]) n = t[0] + t[1] c = Template(n, xe, t, method=method) m = Minuit(c, 1, 1) assert m.limits[0] == (0, np.inf) assert m.limits[1] == (0, np.inf) m.migrad() assert m.valid assert m.ndof == 1 if method == "asy": assert c.errordef == 0.5 assert_equal(m.fmin.reduced_chi2, np.nan) # asy produces values far away from truth in this case assert_allclose(m.values, [1, 3], atol=0.2) else: assert c.errordef == 1.0 assert_allclose(m.fval, 0, atol=1e-4) assert_allclose(m.fmin.reduced_chi2, 0, atol=1e-5) assert_allclose(m.values, [2, 4], atol=1e-2) @pytest.mark.parametrize( "template", ( (np.array([1.0, 1.0, 0.0]), np.array([0.0, 1.0, 3.0])), ( np.array([[1.0, 1.0], [1.0, 1.0], [0.0, 0.0]]), np.array([[0.0, 0.0], [1.0, 1.0], [3.0, 3.0]]), ), ), ) def test_Template_does_not_modify_inputs(template): from copy import deepcopy xe = np.array([0, 1, 2, 3]) template_copy = deepcopy(template) n = template[0] + template[1] Template(n, xe, template, method="da") assert_equal(template, template_copy) def generate(rng, nmc, truth, bins, tf=1, df=1): stats = pytest.importorskip("scipy.stats") xe = np.linspace(0, 2, bins + 1) b = np.diff(stats.truncexpon(1, 0, 2).cdf(xe)) s = np.diff(stats.norm(1, 0.1).cdf(xe)) n = b * truth[0] + s * truth[1] t = b * nmc, s * nmc if rng is not None: n = rng.poisson(n / df) * df if df != 1: n = np.transpose((n, n * df)) t = [rng.poisson(ti / tf) * tf for ti in t] if tf != 1: t = [np.transpose((tj, tj * tf)) for tj in t] return n, xe, np.array(t) @pytest.mark.parametrize("method", ("jsc", "asy", "da")) @pytest.mark.parametrize("with_mask", (False, True)) @pytest.mark.parametrize("weighted_data", (False, True)) def test_Template_weighted(method, with_mask, weighted_data): rng = np.random.default_rng(1) truth = 750, 250 z = [] rng = np.random.default_rng(1) for itoy in range(100): ni, xe, ti = generate(rng, 400, truth, 15, 1.5, 1.5 if weighted_data else 1) c = Template(ni, xe, ti, method=method) if with_mask: cx = 0.5 * (xe[1:] + xe[:-1]) c.mask = cx != 1.5 m = Minuit(c, *truth) m.limits = (0, None) m.strategy = 0 for iter in range(10): m.migrad(iterate=1) m.hesse() if m.valid and m.accurate: break assert m.valid z.append((m.values[1] - truth[1]) / m.errors[1]) assert_allclose(np.mean(z), 0, atol=0.3) assert_allclose(np.std(z), 1, rtol=0.1) def test_Template_bad_input(): with pytest.raises(ValueError): Template([1, 2], [1, 2, 3], []) with pytest.raises(ValueError, match="do not match"): Template([1, 2], [1, 2, 3], [[1, 2, 3], [1, 2, 3]]) with pytest.raises(ValueError, match="do not match"): Template( [1, 2], [1, 2, 3], [[[1, 2], [3, 4]], [[1, 2], [3, 4], [5, 6]]], ) with pytest.raises(ValueError, match="not understood"): Template([1], [1, 2], [[1]], method="foo") with pytest.raises(ValueError, match="number of names"): Template([1], [1, 2], [[1]], name=("b", "s")) with pytest.raises(ValueError, match="model_or_template"): Template([1], [1, 2], [[1], None]) def test_Template_visualize(): pytest.importorskip("matplotlib") xe = [0, 1, 2] n = [1, 2] t = [[1, 2], [5, 4]] c = Template(n, xe, t) c.visualize((1, 2)) c.mask = np.array([False, True]) c.visualize((1, 2)) def test_Template_visualize_2D(): pytest.importorskip("matplotlib") xe = ([0, 1, 2], [0, 1, 2]) n = [[1, 2], [3, 4]] t = [[[1, 2], [1, 2]], [[5, 4], [5, 4]]] c = Template(n, xe, t) c.visualize((1, 2)) def test_Template_pickle(): n = np.array([1, 2, 3]) xe = np.array([0, 1, 2, 3]) t = np.array([[1, 1, 0], [0, 1, 3]]) c = Template(n, xe, t) b = pickle.dumps(c) c2 = pickle.loads(b) assert_equal(c.data, c2.data) def test_Template_with_model(): n = np.array([3, 2, 3]) xe = np.array([0, 1, 2, 3]) t = np.array([0.1, 0, 1]) c = Template(n, xe, (t, scaled_cdf)) assert describe(c) == ["x0", "x1_n", "x1_mu", "x1_sigma"] m = Minuit(c, 1, 1, 0.5, 1) m.limits["x0", "x1_n"] = (0, None) m.limits["x1_sigma"] = (0.1, None) m.limits["x1_mu"] = (xe[0], xe[-1]) m.migrad() assert m.valid c2 = Template(n, xe, (t, scaled_cdf), name=("a", "b", "c", "d")) assert describe(c2) == ["a", "b", "c", "d"] m = Minuit(c2, 1, 1, 0.5, 1) m.limits["d"] = (0.1, None) m.migrad() assert m.valid def test_Template_with_model_2D(): truth1 = (1.0, 0.1, 0.2, 0.3, 0.4, 0.5) x1, y1 = mvnorm(*truth1[1:]).rvs(size=int(truth1[0] * 1000), random_state=1).T truth2 = (1.0, 0.2, 0.1, 0.4, 0.3, 0.0) x2, y2 = mvnorm(*truth2[1:]).rvs(size=int(truth2[0] * 1000), random_state=1).T x = np.append(x1, x2) y = np.append(y1, y2) w, xe, ye = np.histogram2d(x, y, bins=(3, 5)) def model(xy, n, mux, muy, sx, sy, rho): return n * 1000 * mvnorm(mux, muy, sx, sy, rho).cdf(np.transpose(xy)) x3, y3 = mvnorm(*truth2[1:]).rvs(size=int(truth2[0] * 10000), random_state=2).T template = np.histogram2d(x3, y3, bins=(xe, ye))[0] cost = Template(w, (xe, ye), (model, template)) assert cost.ndata == np.prod(w.shape) m = Minuit(cost, *truth1, 1) m.limits["x0_n", "x0_sx", "x0_sy"] = (0, None) m.limits["x0_rho"] = (-1, 1) m.migrad() assert m.valid assert_allclose(m.values, truth1 + (1e3,), rtol=0.1) def test_Template_with_only_models(): n = np.array([3, 2, 3]) xe = np.array([0, 1, 2, 3]) c = Template(n, xe, (lambda x, n, mu: n * expon_cdf(x, mu), scaled_cdf)) assert describe(c) == ["x0_n", "x0_mu", "x1_n", "x1_mu", "x1_sigma"] m = Minuit(c, 1, 0.5, 1, 0.5, 1) m.limits["x0_n", "x1_n", "x1_sigma"] = (0, None) m.limits["x0_mu", "x1_mu"] = (xe[0], xe[-1]) m.migrad() assert m.valid def test_Template_pulls(): rng = np.random.default_rng(1) xe = np.linspace(0, 1, 200) tmu = np.array( [ np.diff(norm_cdf(xe, 0.5, 0.1)) * 1000, np.diff(expon_cdf(xe, 1)) * 10000, ] ) truth = (1000, 2000) mu = sum(truth[i] * (tmu[i] / np.sum(tmu[i])) for i in range(2)) n = rng.poisson(mu) t = rng.poisson(tmu) c = Template(n, xe, t) m = Minuit(c, *truth) m.migrad() assert m.valid pulls = c.pulls(m.values) assert np.nanmean(pulls) == pytest.approx(0, abs=0.1) assert np.nanvar(pulls) == pytest.approx(1, abs=0.1) def test_deprecated(): from iminuit import cost with pytest.warns(np.VisibleDeprecationWarning): from iminuit.cost import BarlowBeestonLite assert BarlowBeestonLite is cost.Template with pytest.warns(np.VisibleDeprecationWarning): from iminuit.cost import barlow_beeston_lite_chi2_jsc assert barlow_beeston_lite_chi2_jsc is cost.template_chi2_jsc with pytest.warns(np.VisibleDeprecationWarning): from iminuit.cost import barlow_beeston_lite_chi2_hpd assert barlow_beeston_lite_chi2_hpd is cost.template_chi2_da def test_deprecated_Template_method(): from iminuit import cost with pytest.warns(np.VisibleDeprecationWarning): t = Template([1], [2, 3], [[1], [2]], method="hpd") t._impl is cost.template_chi2_da @pytest.mark.parametrize("cost", (BinnedNLL, ExtendedBinnedNLL)) def test_binned_cost_with_model_shape_error_message_1D(cost): n = [1, 2] edges = [0.1, 0.2, 0.3] # cdf should return same length as xe, not same length as n def cdf(xe, a): return xe[:-1] c = cost(n, edges, cdf) with pytest.raises( ValueError, match=( r"Expected model to return an array of shape \(3,\), " r"but it returns an array of shape \(2,\)" ), ): c(1) @pytest.mark.parametrize("cost", (BinnedNLL, ExtendedBinnedNLL)) def test_binned_cost_with_model_shape_error_message_2D(cost): n = [[1, 2, 3], [4, 5, 6]] edges = [0.5, 0.6, 0.7], [0.1, 0.2, 0.3, 0.4] def cdf(xye, a): # xye has shape (2, N), returned array should be (N,) return np.ones(xye.shape[1] - 1) c = cost(n, edges, cdf) with pytest.raises( ValueError, match=( r"Expected model to return an array of shape \(12,\), " r"but it returns an array of shape \(11,\)" ), ): c(1) iminuit-2.24.0/tests/test_deprecated.py0000644000000000000000000000124514332717401015062 0ustar00from iminuit._deprecated import deprecated, deprecated_parameter import pytest import numpy as np def test_deprecated_func(): @deprecated("bla") def func(x): pass with pytest.warns(np.VisibleDeprecationWarning, match="func is deprecated: bla"): func(1) def test_deprecated_parameter(): @deprecated_parameter(foo="bar") def some_function(x, y, foo): pass some_function(1, 2, foo=3) with pytest.warns( np.VisibleDeprecationWarning, match="keyword 'bar' is deprecated, please use 'foo'", ): some_function(1, 2, bar=3) with pytest.raises(TypeError): some_function(x=1, baz=3, y=2) iminuit-2.24.0/tests/test_describe.py0000644000000000000000000001313214332717401014540 0ustar00from iminuit.util import describe, make_func_code from iminuit.typing import Annotated, Gt, Lt, Ge, Le, Interval from math import ldexp import platform from functools import wraps import pytest import numpy as np from numpy.typing import NDArray is_pypy = platform.python_implementation() == "PyPy" def test_function(): def f(x, y): pass assert describe(f) == ["x", "y"] def test_class_method(): class A: def f(self, x, y): pass assert describe(A().f) == ["x", "y"] def test_class_unbound_method(): class A: def f(self, x, y): pass assert describe(A.f) == ["self", "x", "y"] def test_functor(): class A: def __call__(self, x, y): pass assert describe(A()) == ["x", "y"] def test_builtin_by_parsing_doc(): assert describe(ldexp) == ["x", "i"] def test_lambda(): assert describe(lambda a, b: 0) == ["a", "b"] def test_generic_function(): def f(*args): pass assert describe(f) == [] def test_generic_partial(): from functools import partial def f(*args): pass partial_f = partial(f, 42, 12, 4) assert describe(partial_f) == [] def test_generic_lambda(): assert describe(lambda *args: 0) == [] def test_generic_class_method(): class A: def f(self, *args): pass assert describe(A().f) == [] def test_generic_functor(): class A: def __call__(self, *args): pass assert describe(A()) == [] def test_generic_functor_with_fake_func(): class A: def __init__(self): self.func_code = make_func_code(["x", "y"]) def __call__(self, *args): pass with pytest.warns(np.VisibleDeprecationWarning): assert describe(A()) == ["x", "y"] def test_decorated_function(): def dummy_decorator(f): @wraps(f) def decorated(*args, **kwargs): return f(*args, **kwargs) return decorated @dummy_decorator def one_arg(x): pass @dummy_decorator def many_arg(x, y, z, t): pass @dummy_decorator def kw_only(x, *, y, z): pass assert describe(one_arg) == list("x") assert describe(many_arg) == list("xyzt") assert describe(kw_only) == list("xyz") def test_ambiguous_1(): def f(x, *args): pass assert describe(f) == ["x"] def test_ambiguous_2(): def f(x, kw=None): pass assert describe(f) == ["x", "kw"] def test_ambiguous_3(): def f(x, **kw): pass assert describe(f) == ["x"] def test_ambiguous_4(): class A: def __call__(self, x, **kw): pass assert describe(A()) == ["x"] def test_from_docstring_1(): def f(*args): """f(x, y, z)""" assert describe(f) == ["x", "y", "z"] def test_from_docstring_2(): class Foo: def bar(self, *args): """Foo.bar(self, int ncall_me =10000, [resume=True, int nsplit=1])""" pass assert describe(Foo().bar) == ["ncall_me", "resume", "nsplit"] def test_from_docstring_3(): assert describe(min) == ["iterable", "default", "key"] def test_from_docstring_4(): def f(*args): """f(a=(), b=[], *args, *[, foo=1])""" assert describe(f) == ["a", "b"] def test_from_bad_docstring_2(): def foo(*args): """foo is some function""" pass assert describe(foo) == [] def test_with_type_hints(): def foo( x: NDArray, a: Annotated[float, Gt(0), Lt(1)], b: float, c: Annotated[float, 0:], d: Annotated[float, Ge(1)], e: Annotated[float, Le(2)], f: Annotated[float, Interval(gt=2, lt=3)], ): ... r = describe(foo, annotations=True) assert r == { "x": None, "a": (0, 1), "b": None, "c": (0, np.inf), "d": (1, np.inf), "e": (-np.inf, 2), "f": (2, 3), } class Foo: def __call__(self, x: NDArray, a: Annotated[float, Gt(0), Lt(1)], b: float): ... r = describe(Foo.__call__, annotations=True) assert r == {"self": None, "x": None, "a": (0, 1), "b": None} r = describe(Foo(), annotations=True) assert r == {"x": None, "a": (0, 1), "b": None} def test_with_pydantic_types(): tp = pytest.importorskip("pydantic.types") def foo( x: NDArray, a: tp.PositiveFloat, b: tp.NonNegativeFloat, c: float, d: Annotated[float, tp.annotated_types.Gt(1)], e: Annotated[float, tp.annotated_types.Interval(gt=0, lt=1)], ): ... r = describe(foo, annotations=True) assert r == { "x": None, "a": (0, np.inf), "b": (0, np.inf), "c": None, "d": (1, np.inf), "e": (0, 1), } def test_with_annotated_types(): tp = pytest.importorskip("annotated_types") def foo( x: NDArray, a: float, b: Annotated[float, tp.Gt(1)], c: Annotated[float, tp.Interval(gt=0, lt=1)], ): ... r = describe(foo, annotations=True) assert r == { "x": None, "a": None, "b": (1, np.inf), "c": (0, 1), } class Foo(float): pass def test_string_annotation_1(): def f(x, mu: "Foo"): pass assert describe(f, annotations=True) == {"x": None, "mu": None} def test_string_annotation_2(): def f(x, mu: "Annotated[float, Gt(1)]"): pass assert describe(f, annotations=True) == {"x": None, "mu": (1, np.inf)} def test_string_annotation_3(): def f(x, mu: "Bar"): # noqa pass assert describe(f, annotations=True) == {"x": None, "mu": None} iminuit-2.24.0/tests/test_draw.py0000644000000000000000000001415514332717401013723 0ustar00import pytest from iminuit import Minuit from pathlib import Path import numpy as np from numpy.testing import assert_allclose import contextlib mpl = pytest.importorskip("matplotlib") plt = pytest.importorskip("matplotlib.pyplot") mpl.use("Agg") def f1(x, y): return (1 - x) ** 2 + np.exp((y - 1) ** 2) @pytest.fixture def minuit(): m = Minuit(f1, x=0, y=0) m.migrad() return m @pytest.fixture def fig(request): fig = plt.figure() yield fig p = Path(__file__).parent / "fig" if not p.exists(): p.mkdir() fig.savefig(p / (request.node.name + ".svg")) plt.close() @pytest.mark.parametrize("arg", ("x", 1)) def test_profile_1(fig, minuit, arg): minuit.draw_profile(arg) plt.ylim(0, 5) def test_profile_2(fig, minuit): minuit.draw_profile("x", grid=np.linspace(0, 5)) @pytest.mark.parametrize("arg", ("x", 1)) def test_mnprofile_1(fig, minuit, arg): # plots with hesse errors minuit.draw_mnprofile(arg) plt.ylim(0, 5) def test_mnprofile_2(fig, minuit): minuit.minos() minuit.draw_mnprofile("x", grid=np.linspace(0, 5)) def test_mncontour_1(fig, minuit): minuit.draw_mncontour("x", "y") def test_mncontour_2(fig, minuit): # use 0, 1 instead of "x", "y" minuit.draw_mncontour(0, 1, cl=0.68) def test_mncontour_3(fig, minuit): minuit.draw_mncontour("x", "y", cl=[0.68, 0.9]) def test_mncontour_4(fig, minuit): minuit.draw_mncontour("x", "y", size=20, interpolated=200) def test_mncontour_5(fig, minuit): minuit.draw_mncontour("x", "y", size=20, interpolated=10) def test_contour_1(fig, minuit): minuit.draw_contour("x", "y") def test_contour_2(fig, minuit): # use 0, 1 instead of "x", "y" minuit.draw_contour(0, 1, size=20, bound=2) def test_contour_3(fig, minuit): minuit.draw_contour("x", "y", size=100, bound=((-0.5, 2.5), (-1, 3))) def test_contour_4(fig, minuit): minuit.draw_contour("x", "y", size=(10, 50), bound=((-0.5, 2.5), (-1, 3))) def test_contour_5(fig, minuit): minuit.draw_contour("x", "y", grid=(np.linspace(-0.5, 2.5), np.linspace(-1, 3))) def test_mnmatrix_1(fig, minuit): minuit.draw_mnmatrix() def test_mnmatrix_2(fig, minuit): minuit.draw_mnmatrix(cl=[0.68, 0.9]) def test_mnmatrix_3(fig): m = Minuit(lambda x: x**2, x=0) m.migrad() m.draw_mnmatrix() def test_mnmatrix_4(fig, minuit): with pytest.raises(ValueError): minuit.draw_mnmatrix(cl=[]) def test_mnmatrix_5(): m = Minuit(lambda x: x**2, x=10) with pytest.raises(RuntimeError, match="minimum is not valid"): m.draw_mnmatrix() def test_mnmatrix_6(fig, minuit): minuit.fixed = True with pytest.raises(RuntimeError, match="all parameters are fixed"): minuit.draw_mnmatrix() def test_mnmatrix_7(fig): # provoke an mnprofile iteration on asymmetric profile m = Minuit(lambda x: abs(x) ** 2 + x**4 + 10 * x, x=0) m.migrad() m.draw_mnmatrix(cl=[1, 3]) @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_interactive(): ipywidgets = pytest.importorskip("ipywidgets") def cost(a, b): return a**2 + b**2 class Plot: def __init__(self): self.called = False self.raises = False def __call__(self, args): self.called = True if self.raises: raise ValueError("foo") @contextlib.contextmanager def assert_call(self): self.called = False yield assert self.called plot = Plot() m = Minuit(cost, 1, 1) with pytest.raises(AttributeError, match="no visualize method"): m.interactive(raise_on_exception=True) with plot.assert_call(): out1 = m.interactive(plot) assert isinstance(out1, ipywidgets.HBox) # manipulate state to also check this code ui = out1.children[1] header, parameters = ui.children fit_button, update_button, reset_button, algo_select = header.children with plot.assert_call(): fit_button.click() assert_allclose(m.values, (0, 0), atol=1e-5) with plot.assert_call(): reset_button.click() assert_allclose(m.values, (1, 1), atol=1e-5) algo_select.value = "Scipy" with plot.assert_call(): fit_button.click() algo_select.value = "Simplex" with plot.assert_call(): fit_button.click() update_button.value = False with plot.assert_call(): parameters.children[0].slider.value = 0.4 # change first slider parameters.children[0].fix.value = True with plot.assert_call(): parameters.children[0].opt.value = True class Cost: def visualize(self, args): return plot(args) def __call__(self, a, b): return (a - 100) ** 2 + (b + 100) ** 2 c = Cost() m = Minuit(c, 0, 0) with plot.assert_call(): out = m.interactive(raise_on_exception=True) # this should modify slider range ui = out.children[1] header, parameters = ui.children fit_button, update_button, reset_button, algo_select = header.children assert parameters.children[0].slider.max < 100 assert parameters.children[1].slider.min > -100 with plot.assert_call(): fit_button.click() assert_allclose(m.values, (100, -100), atol=1e-5) # this should trigger an exception plot.raises = True with plot.assert_call(): fit_button.click() @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_interactive_raises(): pytest.importorskip("ipywidgets") def raiser(args): raise ValueError m = Minuit(lambda x, y: 0, 0, 1) # by default do not raise m.interactive(raiser) with pytest.raises(ValueError): m.interactive(raiser, raise_on_exception=True) @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_interactive_with_array_func(): pytest.importorskip("ipywidgets") def cost(par): return par[0] ** 2 + (par[1] / 2) ** 2 class TraceArgs: nargs = 0 def __call__(self, par): self.nargs = len(par) trace_args = TraceArgs() m = Minuit(cost, (1, 2)) m.interactive(trace_args) assert trace_args.nargs == 1 iminuit-2.24.0/tests/test_experimental.py0000644000000000000000000000060414332717401015455 0ustar00from iminuit import util, experimental def test_expanded(): def f(x, y, z): return x + y + z def g(x, a, b): return x + a + b f2, g2 = experimental.expanded(f, g) assert f(1, 2, 3) + g(1, 4, 5) == f2(1, 2, 3, 4, 5) + g2(1, 2, 3, 4, 5) assert util.describe(f2) == ["x", "y", "z", "a", "b"] assert util.describe(g2) == ["x", "y", "z", "a", "b"] iminuit-2.24.0/tests/test_fmin_bad.txt0000644000000000000000000000301414332717401014704 0ustar00┌──────────────────────────────────┬──────────────────────────────────────┐ │ FCN = nan │ Nfcn = 100000 │ │ EDM = 1.23e-10 (Goal: 1e-05) │ Ngrad = 200000 │ ├───────────────┬──────────────────┼──────────────────────────────────────┤ │INVALID Minimum│INVALID Parameters│ SOME Parameters at limit │ ├───────────────┴──────────────────┼──────────────────────────────────────┤ │ ABOVE EDM threshold (goal x 10) │ ABOVE call limit │ ├───────────────┬──────────────────┼───────────┬─────────────┬────────────┤ │ Hesse FAILED │ NO Covariance │APPROXIMATE│NOT pos. def.│ FORCED │ └───────────────┴──────────────────┴───────────┴─────────────┴────────────┘ iminuit-2.24.0/tests/test_functions.py0000644000000000000000000000306614332717401014775 0ustar00from iminuit import Minuit from iminuit.testing import ( rosenbrock, rosenbrock_grad, ackley, beale, matyas, sphere_np, ) import numpy as np from numpy.testing import assert_allclose import pytest @pytest.mark.parametrize("grad", (None, rosenbrock_grad)) def test_rosenbrock(grad): m = Minuit(rosenbrock, x=0, y=0, grad=grad) m.tol = 1e-4 m.migrad() assert_allclose(m.fval, 0, atol=1e-6) assert_allclose(m.values["x"], 1.0, atol=1e-3) assert_allclose(m.values["y"], 1.0, atol=1e-3) def test_ackley(): m = Minuit(ackley, x=0.3, y=-0.2) # m.errors = 1.7 m.tol = 1e-4 m.migrad() assert_allclose(m.fval, 0, atol=1e-6) assert_allclose(m.values, [0, 0], atol=1e-6) def test_beale(): m = Minuit(beale, x=0.5, y=0.25) m.tol = 1e-4 m.migrad() assert_allclose(m.fval, 0, atol=1e-6) assert_allclose(m.values, [3, 0.5], atol=1e-3) def test_matyas(): m = Minuit(matyas, x=0.5, y=0.5) m.tol = 1e-4 m.migrad() assert_allclose(m.fval, 0, atol=1e-14) assert_allclose(m.values, [0, 0], atol=1e-14) def test_matyas_oneside(): """One-sided limit when the minimum is in the forbidden region.""" m = Minuit(matyas, x=2.5, y=0.5) m.tol = 1e-4 m.limits["x"] = (1, None) m.migrad() assert_allclose(m.values, [1, 0.923], atol=1e-3) @pytest.mark.parametrize("start", (2, (2, 2, 2))) def test_sphere_np(start): m = Minuit(sphere_np, start) m.migrad() assert_allclose(m.fval, 0, atol=1e-6) assert_allclose(m.values, np.zeros_like(start), atol=1e-3) iminuit-2.24.0/tests/test_issue.py0000644000000000000000000001023414332717401014110 0ustar00import numpy as np def test_issue_424(): from iminuit import Minuit def fcn(x, y, z): return (x - 1) ** 2 + (y - 4) ** 2 / 2 + (z - 9) ** 2 / 3 m = Minuit(fcn, x=0.0, y=0.0, z=0.0) m.migrad() m.fixed["x"] = True m.errors["x"] = 2 m.hesse() # this used to release x assert m.fixed["x"] assert m.errors["x"] == 2 def test_issue_544(): import pytest from iminuit import Minuit from iminuit.util import IMinuitWarning def fcn(x, y): return x**2 + y**2 m = Minuit(fcn, x=0, y=0) m.fixed = True with pytest.warns(IMinuitWarning): m.hesse() # this used to cause a segfault def test_issue_648(): from iminuit import Minuit class F: first = True def __call__(self, a, b): if self.first: assert a == 1.0 and b == 2.0 self.first = False return a**2 + b**2 m = Minuit(F(), a=1, b=2) m.fixed["a"] = False # this used to change a to b m.migrad() def test_issue_643(): from iminuit import Minuit def fcn(x, y, z): return (x - 2) ** 2 + (y - 3) ** 2 + (z - 4) ** 2 m = Minuit(fcn, x=2, y=3, z=4) m.migrad() m2 = Minuit(fcn, x=m.values["x"], y=m.values["y"], z=m.values["z"]) # used to call MnHesse when it was not needed and quickly exhaust call limit for i in range(10): m2.minos() m2.reset() # used to exhaust call limit, because calls to MnHesse did not reset call count for i in range(10): m2.values = m.values m2.minos() def test_issue_669(): from iminuit import Minuit def fcn(x, y): return x**2 + (y / 2) ** 2 m = Minuit(fcn, x=0, y=0) m.migrad() xy1 = m.mncontour(x="x", y="y", size=10) xy2 = m.mncontour(x="y", y="x", size=10) # used to fail # needs better way to compare polygons for x, y in xy1: match = False for y2, x2 in xy2: if abs(x - x2) < 1e-3 and abs(y - y2) < 1e-3: match = True break assert match # cannot define this inside function, pickle will not allow it def fcn(par): return np.sum(par**2) # cannot define this inside function, pickle will not allow it def grad(par): return 2 * par def test_issue_687(): import pickle import numpy as np from iminuit import Minuit start = np.zeros(3) m = Minuit(fcn, start) m.migrad() s_m = str(m) s = pickle.dumps(m) m2 = pickle.loads(s) s_m2 = str(m2) # this used to fail assert s_m == s_m2 def test_issue_694(): import pytest import numpy as np from iminuit import Minuit from iminuit.cost import ExtendedUnbinnedNLL stats = pytest.importorskip("scipy.stats") xmus = 1.0 xmub = 5.0 xsigma = 1.0 ymu = 0.5 ysigma = 0.2 ytau = 0.1 for seed in range(100): rng = np.random.default_rng(seed) xs = rng.normal(xmus, xsigma, size=33) xb = rng.normal(xmub, xsigma, size=66) x = np.append(xs, xb) def model(x, sig_n, sig_mu, sig_sigma, bkg_n, bkg_tau): return sig_n + bkg_n, ( sig_n * stats.norm.pdf(x, sig_mu, sig_sigma) + bkg_n * stats.expon.pdf(x, 0, bkg_tau) ) nll = ExtendedUnbinnedNLL(x, model) m = Minuit(nll, sig_n=33, sig_mu=ymu, sig_sigma=ysigma, bkg_n=66, bkg_tau=ytau) m.migrad() if np.isnan(m.fmin.edm): assert not m.valid assert m.fmin.is_above_max_edm break else: assert False def test_issue_923(): from iminuit import Minuit from iminuit.cost import LeastSquares import numpy as np import pytest # implicitly needed by visualize pytest.importorskip("matplotlib") def model(x, c1): c2 = 100 res = np.zeros(len(x)) mask = x < 47 res[mask] = c1 res[~mask] = c2 return res xtest = np.linspace(0, 74) ytest = xtest * 0 + 1 ytesterr = ytest least_squares = LeastSquares(xtest, ytest, ytesterr, model) m = Minuit(least_squares, c1=1) m.migrad() # this used to trigger an endless (?) loop m.visualize() iminuit-2.24.0/tests/test_minimize.py0000644000000000000000000000777214332717401014616 0ustar00import pytest from iminuit import minimize import numpy as np from numpy.testing import assert_allclose, assert_equal opt = pytest.importorskip("scipy.optimize") def func(x, *args): c = args[0] if args else 1 return c + x[0] ** 2 + (x[1] - 1) ** 2 + (x[2] - 2) ** 2 def grad(x, *args): return 2 * (x - (0, 1, 2)) def test_simple(): result = minimize(func, (1, 1, 1)) assert_allclose(result.x, (0, 1, 2), atol=1e-8) assert_allclose(result.fun, 1) assert result.nfev > 0 assert result.njev == 0 def test_gradient(): result = minimize(func, (1, 1, 1), jac=grad) assert_allclose(result.x, (0, 1, 2), atol=1e-8) assert_allclose(result.fun, 1) assert result.nfev > 0 assert result.njev > 0 def test_args(): result = minimize(func, np.ones(3), args=(5,)) assert_allclose(result.x, (0, 1, 2), atol=1e-8) assert_allclose(result.fun, 5) assert result.nfev > 0 assert result.njev == 0 def test_callback(): trace = [] result = minimize(func, np.ones(3), callback=lambda x: trace.append(x.copy())) assert_allclose(result.x, (0, 1, 2), atol=1e-8) assert_allclose(result.fun, 1) assert result.nfev == len(trace) assert_allclose(trace[0], np.ones(3), atol=1e-2) assert_allclose(trace[-1], result.x, atol=1e-2) def test_tol(): ref = np.ones(2) def rosen(par): x, y = par return (1 - x) ** 2 + 100 * (y - x**2) ** 2 r1 = minimize(rosen, (0, 0), tol=1) r2 = minimize(rosen, (0, 0), tol=1e-6) assert max(np.abs(r2.x - ref)) < max(np.abs(r1.x - ref)) def test_disp(capsys): minimize(lambda x: x**2, 0) assert capsys.readouterr()[0] == "" minimize(lambda x: x**2, 0, options={"disp": True}) assert capsys.readouterr()[0] != "" def test_hessinv(): r = minimize(func, (1, 1, 1)) href = np.zeros((3, 3)) for i in range(3): href[i, i] = 0.5 assert_allclose(r.hess_inv, href, atol=1e-8) def test_unsupported(): with pytest.raises(ValueError): minimize(func, (1, 1, 1), constraints=[]) with pytest.raises(ValueError): minimize(func, (1, 1, 1), jac=True) def test_call_limit(): ref = minimize(func, (1, 1, 1)) with pytest.warns(UserWarning): r1 = minimize(func, (1, 1, 1), options={"maxiter": 1}) assert r1.nfev < ref.nfev assert not r1.success assert "Call limit" in r1.message with pytest.warns(DeprecationWarning): r2 = minimize(func, (1, 1, 1), options={"maxfev": 1}) assert not r2.success assert r2.nfev == r1.nfev r3 = minimize(func, (1, 1, 1), options={"maxfun": 1}) assert not r3.success assert r3.nfev == r1.nfev def test_eps(): ref = minimize(func, (1, 1, 1)) r = minimize(func, (1, 1, 1), options={"eps": 1e-10}) assert np.any(ref.x != r.x) assert_allclose(r.x, ref.x, atol=1e-9) def test_bad_function(): class Fcn: n = 0 def __call__(self, x): self.n += 1 return x**2 + 1e-2 * (self.n % 3) r = minimize(Fcn(), [1], options={"maxfun": 100000000}) assert not r.success assert "Estimated distance to minimum too large" in r.message def test_bounds(): r1 = minimize(func, (1.5, 1.7, 1.5), bounds=opt.Bounds((1, 1.5, 1), (2, 2, 2))) assert r1.success assert_allclose(r1.x, (1, 1.5, 2), atol=1e-2) r2 = minimize(func, (1.5, 1.7, 1.5), bounds=((1, 2), (1.5, 2), (1, 2))) assert r2.success assert_equal(r1.x, r2.x) def test_method_warn(): with pytest.raises(ValueError): minimize(func, (1.5, 1.7, 1.5), method="foo") def test_hess_warn(): with pytest.warns(UserWarning): minimize(func, (1.5, 1.7, 1.5), hess=True) def test_unreliable_uncertainties(): r = minimize(func, (1.5, 1.7, 1.5), options={"stra": 0}) assert ( r.message == "Optimization terminated successfully, but uncertainties are unrealiable." ) def test_simplex(): r = minimize(func, (1.5, 1.7, 1.5), method="simplex", tol=1e-4) assert r.success assert_allclose(r.x, (0, 1, 2), atol=2e-3) iminuit-2.24.0/tests/test_minuit.py0000644000000000000000000012377114332717401014300 0ustar00import platform import pytest import numpy as np from numpy.testing import assert_allclose, assert_equal from iminuit import Minuit from iminuit.util import Param, make_func_code from iminuit.warnings import IMinuitWarning from iminuit.typing import Annotated from pytest import approx from argparse import Namespace @pytest.fixture def debug(): from iminuit._core import MnPrint prev = MnPrint.global_level MnPrint.global_level = 3 MnPrint.show_prefix_stack(True) yield MnPrint.global_level = prev MnPrint.show_prefix_stack(False) is_pypy = platform.python_implementation() == "PyPy" def test_version(): import iminuit assert iminuit.__version__ def func0(x, y): # values = (2.0, 5.0), errors = (2.0, 1.0) return (x - 2.0) ** 2 / 4.0 + np.exp((y - 5.0) ** 2) + 10 def func0_grad(x, y): dfdx = (x - 2.0) / 2.0 dfdy = 2.0 * (y - 5.0) * np.exp((y - 5.0) ** 2) return [dfdx, dfdy] class Func1: errordef = 4 def __call__(self, x, y): return func0(x, y) * 4 class Func2: errordef = 4 def __init__(self): self.func_code = make_func_code(["x", "y"]) def __call__(self, *arg): return func0(arg[0], arg[1]) * 4 def func4(x, y, z): return 0.2 * (x - 2.0) ** 2 + 0.1 * (y - 5.0) ** 2 + 0.25 * (z - 7.0) ** 2 + 10 def func4_grad(x, y, z): dfdx = 0.4 * (x - 2.0) dfdy = 0.2 * (y - 5.0) dfdz = 0.5 * (z - 7.0) return dfdx, dfdy, dfdz def func5(x, long_variable_name_really_long_why_does_it_has_to_be_this_long, z): return ( (x - 1) ** 2 + long_variable_name_really_long_why_does_it_has_to_be_this_long**2 + (z + 1) ** 2 ) def func5_grad(x, long_variable_name_really_long_why_does_it_has_to_be_this_long, z): dfdx = 2 * (x - 1) dfdy = 2 * long_variable_name_really_long_why_does_it_has_to_be_this_long dfdz = 2 * (z + 1) return dfdx, dfdy, dfdz def func6(x, m, s, a): return a / ((x - m) ** 2 + s**2) class Correlated: def __init__(self): sx = 2 sy = 1 corr = 0.5 cov = (sx**2, corr * sx * sy), (corr * sx * sy, sy**2) self.cinv = np.linalg.inv(cov) def __call__(self, x): return np.dot(x.T, np.dot(self.cinv, x)) def func_np(x): # test numpy support return np.sum((x - 1) ** 2) def func_np_grad(x): # test numpy support return 2 * (x - 1) data_y = [ 0.552, 0.735, 0.846, 0.875, 1.059, 1.675, 1.622, 2.928, 3.372, 2.377, 4.307, 2.784, 3.328, 2.143, 1.402, 1.44, 1.313, 1.682, 0.886, 0.0, 0.266, 0.3, ] data_x = list(range(len(data_y))) def func_test_helper(f, grad=None, errordef=None): m = Minuit(f, x=0, y=0, grad=grad) if errordef: m.errordef = errordef m.migrad() val = m.values assert_allclose(val["x"], 2.0, rtol=2e-3) assert_allclose(val["y"], 5.0, rtol=2e-3) assert_allclose(m.fval, 11.0 * m.errordef, rtol=1e-3) assert m.valid assert m.accurate m.hesse() err = m.errors assert_allclose(err["x"], 2.0, rtol=1e-3) assert_allclose(err["y"], 1.0, rtol=1e-3) m.errors = (1, 2) assert_allclose(err["x"], 1.0, rtol=1e-3) assert_allclose(err["y"], 2.0, rtol=1e-3) return m def test_mncontour_interpolated_1(): m = Minuit(func0, 1, 1) m.migrad() # interpolated < size is ignored pts = m.mncontour("x", "y", size=20, interpolated=10) assert len(pts) == 21 def test_mncontour_interpolated_2(): pytest.importorskip("scipy.interpolate") m = Minuit(func0, 1, 1) m.migrad() pts = m.mncontour("x", "y", size=20, interpolated=200) assert len(pts) == 200 def test_func0(): m1 = func_test_helper(func0) m2 = func_test_helper(func0, grad=func0_grad) assert m1.ngrad == 0 assert m2.ngrad > 0 # check that providing gradient improves convergence assert m2.nfcn < m1.nfcn def test_lambda(): func_test_helper(lambda x, y: func0(x, y)) def test_Func1(): func_test_helper(Func1()) def test_Func2(): with pytest.warns(np.VisibleDeprecationWarning): func_test_helper(Func2()) def test_no_signature(): def no_signature(*args): x, y = args return (x - 1) ** 2 + (y - 2) ** 2 m = Minuit(no_signature, 3, 4) assert m.values == (3, 4) assert m.parameters == ("x0", "x1") m = Minuit(no_signature, x=1, y=2, name=("x", "y")) assert m.values == (1, 2) m.migrad() val = m.values assert_allclose((val["x"], val["y"], m.fval), (1, 2, 0), atol=1e-8) assert m.valid with pytest.raises(RuntimeError): Minuit(no_signature, x=1) with pytest.raises(RuntimeError): Minuit(no_signature, x=1, y=2) def test_use_array_call(): inf = float("infinity") m = Minuit( func_np, (1, 1), name=("a", "b"), ) m.fixed = False m.errors = 1 m.limits = (0, inf) m.migrad() assert m.parameters == ("a", "b") assert_allclose(m.values, (1, 1)) m.hesse() c = m.covariance assert_allclose((c[("a", "a")], c[("b", "b")]), (1, 1)) with pytest.raises(RuntimeError): Minuit(lambda *args: 0, [1, 2], name=["a", "b", "c"]) def test_release_with_none(): m = Minuit(func0, x=0, y=0) m.fixed = (True, False) assert m.fixed == (True, False) m.fixed = None assert m.fixed == (False, False) def test_parameters(): m = Minuit(lambda a, b: 0, a=1, b=1) assert m.parameters == ("a", "b") assert m.pos2var == ("a", "b") assert m.var2pos["a"] == 0 assert m.var2pos["b"] == 1 def test_covariance(): m = Minuit(func0, x=0, y=0) assert m.covariance is None m.migrad() c = m.covariance assert_allclose((c["x", "x"], c["y", "y"]), (4, 1), rtol=1e-4) assert_allclose((c[0, 0], c[1, 1]), (4, 1), rtol=1e-4) expected = [[4.0, 0.0], [0.0, 1.0]] assert_allclose(c, expected, atol=1e-4) assert isinstance(c, np.ndarray) assert c.shape == (2, 2) c = c.correlation() expected = [[1.0, 0.0], [0.0, 1.0]] assert_allclose(c, expected, atol=1e-4) assert c["x", "x"] == approx(1.0) def test_array_func_1(): m = Minuit(func_np, (2, 1)) m.errors = (1, 1) assert m.parameters == ("x0", "x1") assert m.values == (2, 1) assert m.errors == (1, 1) m.migrad() assert_allclose(m.values, (1, 1), rtol=1e-2) c = m.covariance assert_allclose(np.diag(c), (1, 1), rtol=1e-2) def test_array_func_2(): m = Minuit(func_np, (2, 1), grad=func_np_grad, name=("a", "b")) m.fixed = (False, True) m.errors = (0.5, 0.5) m.limits = ((0, 2), (-np.inf, np.inf)) assert m.values == (2, 1) assert m.errors == (0.5, 0.5) assert m.fixed == (False, True) assert m.limits["a"] == (0, 2) m.migrad() assert m.fmin.ngrad > 0 assert_allclose(m.values, (1, 1), rtol=1e-2) c = m.covariance assert_allclose(c, ((1, 0), (0, 0)), rtol=1e-2) m.minos() assert len(m.merrors) == 1 assert m.merrors[0].lower == approx(-1, abs=1e-2) assert m.merrors[0].name == "a" def test_wrong_use_of_array_init(): m = Minuit(lambda a, b: a**2 + b**2, (1, 2)) with pytest.raises(TypeError): m.migrad() def test_reset(): m = Minuit(func0, x=0, y=0) m.migrad() n = m.nfcn m.migrad() assert m.nfcn > n m.reset() m.migrad() assert m.nfcn == n m = Minuit(func0, grad=func0_grad, x=0, y=0) m.migrad() n = m.nfcn k = m.ngrad m.migrad() assert m.nfcn > n assert m.ngrad > k m.reset() m.migrad() assert m.nfcn == n assert m.ngrad == k def test_typo(): with pytest.raises(RuntimeError): Minuit(lambda x: 0, y=1) m = Minuit(lambda x: 0, x=0) with pytest.raises(KeyError): m.errors["y"] = 1 with pytest.raises(KeyError): m.limits["y"] = (0, 1) def test_initial_guesses(): m = Minuit(lambda x: 0, x=0) assert m.values["x"] == 0 assert m.errors["x"] == 0.1 m = Minuit(lambda x: 0, x=1) assert m.values["x"] == 1 assert m.errors["x"] == 1e-2 @pytest.mark.parametrize("grad", (None, func0_grad)) def test_fixed(grad): m = Minuit(func0, grad=grad, x=0, y=0) assert m.npar == 2 assert m.nfit == 2 m.migrad() m.minos() assert_allclose(m.values, (2, 5), rtol=2e-3) assert_allclose(m.errors, (2, 1), rtol=1e-4) assert_allclose(m.covariance, ((4, 0), (0, 1)), atol=1e-4) m = Minuit(func0, grad=grad, x=0, y=10) assert not m.fixed["y"] m.fixed["y"] = True assert m.fixed["y"] assert m.npar == 2 assert m.nfit == 1 m.migrad() assert_allclose(m.values, (2, 10), rtol=1e-2) assert_allclose(m.fval, func0(2, 10)) assert m.fixed == [False, True] assert_allclose(m.covariance, [[4, 0], [0, 0]], atol=3e-4 if grad is None else 3e-2) assert not m.fixed["x"] assert m.fixed["y"] m.fixed["x"] = True m.fixed["y"] = False assert m.npar == 2 assert m.nfit == 1 m.migrad() m.hesse() assert_allclose(m.values, (2, 5), rtol=1e-2) assert_allclose(m.covariance, [[0, 0], [0, 1]], atol=1e-4) with pytest.raises(KeyError): m.fixed["a"] # fix by setting limits m = Minuit(func0, x=0, y=10.0) m.limits["y"] = (10, 10) assert m.fixed["y"] assert m.npar == 2 assert m.nfit == 1 # initial value out of range is forced in range m = Minuit(func0, x=0, y=20.0) m.limits["y"] = (10, 10) assert m.fixed["y"] assert m.values["y"] == 10 assert m.npar == 2 assert m.nfit == 1 m.fixed = True assert m.fixed == [True, True] m.fixed[1:] = False assert m.fixed == [True, False] assert m.fixed[:1] == [True] def test_fixto(): m = Minuit(func0, x=0, y=0) assert np.all(~m.fixed) m.fixto(0, 1) assert m.fixed[0] assert m.values[0] == 1 m.fixto([0, 1], 0) assert np.all(m.fixed) assert m.values == [0, 0] m.fixed = False assert np.all(~m.fixed) m.fixto(slice(0, 2), [1, 2]) assert np.all(m.fixed) assert_equal(m.values, [1, 2]) m.fixed = False assert np.all(~m.fixed) m.fixto(..., [2, 3]) assert np.all(m.fixed) assert_equal(m.values, [2, 3]) with pytest.raises(ValueError, match="length of argument"): m.fixto([1], [1, 2]) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_minos(grad): m = Minuit(func0, grad=grad, x=0, y=0) m.migrad() m.minos() assert len(m.merrors) == 2 assert m.merrors["x"].lower == approx(-m.errors["x"], abs=4e-3) assert m.merrors["x"].upper == approx(m.errors["x"], abs=4e-3) assert m.merrors[1].lower == m.merrors["y"].lower assert m.merrors[-1].upper == m.merrors["y"].upper @pytest.mark.parametrize("cl", (0.68, 0.90, 1, 1.5, 2)) @pytest.mark.parametrize("k", (10, 1000)) @pytest.mark.parametrize("limit", (False, True)) def test_minos_cl(cl, k, limit): opt = pytest.importorskip("scipy.optimize") stats = pytest.importorskip("scipy.stats") def nll(lambd): return lambd - k * np.log(lambd) # find location of min + up by hand def crossing(x): return nll(k + x) - (nll(k) + up) if cl >= 1: bound = cl * k**0.5 up = 0.5 * cl**2 else: bound = (stats.chi2(1).ppf(cl) * k) ** 0.5 up = 0.5 * stats.chi2(1).ppf(cl) bound *= 1.5 upper = opt.root_scalar(crossing, bracket=(0, bound)).root lower = opt.root_scalar(crossing, bracket=(-bound, 0)).root m = Minuit(nll, lambd=k) m.limits["lambd"] = (0, None) if limit else None m.errordef = Minuit.LIKELIHOOD m.migrad() assert m.valid assert m.accurate m.minos(cl=cl) assert m.values["lambd"] == approx(k) assert m.errors["lambd"] == approx(k**0.5, abs=2e-3 if limit else None) assert m.merrors["lambd"].lower == approx(lower, rel=1e-3) assert m.merrors["lambd"].upper == approx(upper, rel=1e-3) assert m.merrors[0].lower == m.merrors["lambd"].lower assert m.merrors[-1].upper == m.merrors["lambd"].upper with pytest.raises(KeyError): m.merrors["xy"] with pytest.raises(KeyError): m.merrors["z"] with pytest.raises(IndexError): m.merrors[1] with pytest.raises(IndexError): m.merrors[-2] def test_minos_some_fix(): m = Minuit(func0, x=0, y=0) m.fixed["x"] = True m.migrad() m.minos() assert "x" not in m.merrors me = m.merrors["y"] assert me.name == "y" assert me.lower == approx(-0.83, abs=1e-2) assert me.upper == approx(0.83, abs=1e-2) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_minos_single(grad): m = Minuit(func0, grad=func0_grad, x=0, y=0) m.strategy = 0 m.migrad() m.minos("x") assert len(m.merrors) == 1 me = m.merrors["x"] assert me.name == "x" assert me.lower == approx(-2, rel=2e-3) assert me.upper == approx(2, rel=2e-3) def test_minos_single_fixed(): m = Minuit(func0, x=0, y=0) m.fixed["x"] = True m.migrad() m.minos(1) assert len(m.merrors) == 1 me = m.merrors["y"] assert me.name == "y" assert me.lower == approx(-0.83, abs=1e-2) def test_minos_single_fixed_raising(): m = Minuit(func0, x=0, y=0) m.fixed["x"] = True m.migrad() with pytest.warns(RuntimeWarning): m.minos("x") assert len(m.merrors) == 0 assert m.fixed["x"] m.minos() assert len(m.merrors) == 1 assert "y" in m.merrors def test_minos_single_no_migrad(): m = Minuit(func0, x=0, y=0) with pytest.raises(RuntimeError): m.minos("x") def test_minos_single_nonsense_variable(): m = Minuit(func0, x=0, y=0) m.migrad() with pytest.raises(ValueError): m.minos("nonsense") def test_minos_with_bad_fmin(): m = Minuit(lambda x: 0, x=0) m.migrad() with pytest.raises(RuntimeError): m.minos() def test_minos_bad_index(): m = Minuit(func0, 1, 1) m.migrad() with pytest.raises(ValueError): m.minos(2) @pytest.mark.parametrize("grad", (None, func5_grad)) def test_fixing_long_variable_name(grad): m = Minuit( func5, grad=grad, long_variable_name_really_long_why_does_it_has_to_be_this_long=2, x=0, z=0, ) m.fixed["long_variable_name_really_long_why_does_it_has_to_be_this_long"] = True m.migrad() assert_allclose(m.values, [1, 2, -1], atol=1e-3) def test_initial_value(): m = Minuit(func0, x=1.0, y=2.0) assert_allclose(m.values[0], 1.0) assert_allclose(m.values[1], 2.0) assert_allclose(m.values["x"], 1.0) assert_allclose(m.values["y"], 2.0) m = Minuit(func0, 1.0, 2.0) assert_allclose(m.values[0], 1.0) assert_allclose(m.values[1], 2.0) assert_allclose(m.values["x"], 1.0) assert_allclose(m.values["y"], 2.0) m = Minuit(func0, (1.0, 2.0)) assert_allclose(m.values[0], 1.0) assert_allclose(m.values[1], 2.0) assert_allclose(m.values["x"], 1.0) assert_allclose(m.values["y"], 2.0) with pytest.raises(RuntimeError): Minuit(func0, 1, y=2) with pytest.raises(RuntimeError): Minuit(func0) @pytest.mark.parametrize("grad", (None, func0_grad)) @pytest.mark.parametrize("cl", (None, 0.5, 0.9, 1, 1.5, 2)) @pytest.mark.parametrize("experimental", (False, True)) def test_mncontour(grad, cl, experimental): stats = pytest.importorskip("scipy.stats") m = Minuit(func0, grad=grad, x=1.0, y=2.0) m.migrad() ctr = m.mncontour("x", "y", size=30, cl=cl, experimental=experimental) if cl is None: cl = 0.68 elif cl >= 1: cl = stats.chi2(1).cdf(cl**2) factor = stats.chi2(2).ppf(cl) cl2 = stats.chi2(1).cdf(factor) assert len(ctr) == 31 assert len(ctr[0]) == 2 m.minos("x", "y", cl=cl2) xm = m.merrors["x"] ym = m.merrors["y"] cmin = np.min(ctr, axis=0) cmax = np.max(ctr, axis=0) x, y = m.values assert_allclose((x + xm.lower, y + ym.lower), cmin, atol=1e-2) assert_allclose((x + xm.upper, y + ym.upper), cmax, atol=1e-2) @pytest.mark.parametrize("experimental", (False, True)) def test_mncontour_limits(experimental): pytest.importorskip("scipy.optimize") def cost(x, y): return x**2 + y**2 m = Minuit(cost, x=0.5, y=0.5) m.limits = (0, 2) m.migrad() cont = m.mncontour(0, 1, size=30, experimental=experimental) assert np.all(cont[:, 0] >= 0) assert np.all(cont[:, 1] >= 0) def test_mncontour_no_fmin(): m = Minuit(func0, x=0, y=0) with pytest.raises(RuntimeError): # fails, because this is not a minimum m.mncontour("x", "y") # succeeds m.values = (2, 5) # use 0, 1 instead of "x", "y" c = m.mncontour(0, 1, size=10) # compute reference to compare with m2 = Minuit(func0, x=0, y=0) m2.migrad() c2 = m.mncontour("x", "y", size=10) assert_allclose(c, c2) def test_mncontour_with_fixed_var(): m = Minuit(func0, x=0, y=0) m.fixed["x"] = True m.migrad() with pytest.raises(ValueError): m.mncontour("x", "y") @pytest.mark.parametrize("experimental", (False, True)) def test_mncontour_array_func(experimental): stats = pytest.importorskip("scipy.stats") m = Minuit(Correlated(), (0, 0), name=("x", "y")) m.migrad() cl = stats.chi2(2).cdf(1) ctr = m.mncontour("x", "y", size=30, cl=cl, experimental=experimental) assert len(ctr) == 31 assert len(ctr[0]) == 2 m.minos("x", "y") x, y = m.values xm = m.merrors["x"] ym = m.merrors["y"] cmin = np.min(ctr, axis=0) cmax = np.max(ctr, axis=0) assert_allclose((x + xm.lower, y + ym.lower), cmin, atol=1e-2) assert_allclose((x + xm.upper, y + ym.upper), cmax, atol=1e-2) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_contour(grad): m = Minuit(func0, grad=grad, x=1.0, y=2.0) m.migrad() x, y, v = m.contour("x", "y") X, Y = np.meshgrid(x, y) assert_allclose(func0(X, Y), v.T) def test_contour_separate_size(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() x, y, v = m.contour("x", "y", size=(10, 20)) assert len(x) == 10 assert len(y) == 20 X, Y = np.meshgrid(x, y) assert_allclose(func0(X, Y), v.T) def test_contour_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() x, y, v = m.contour("x", "y", grid=(np.linspace(0, 2, 10), np.linspace(0, 4, 20))) assert len(x) == 10 assert len(y) == 20 X, Y = np.meshgrid(x, y) assert_allclose(func0(X, Y), v.T) def test_contour_bad_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() with pytest.raises(ValueError): m.contour("x", "y", grid=([1, 2, 3], [[1, 2, 3]])) with pytest.raises(ValueError): m.contour("x", "y", grid=([1, 2, 3],)) with pytest.raises(ValueError): m.contour("x", "y", grid=([1, 2, 3], [1, 2], [3, 4])) with pytest.raises(ValueError): m.contour("x", "y", grid=(10, [1, 2, 3])) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_profile(grad): m = Minuit(func0, grad=grad, x=1.0, y=2.0) m.migrad() y, v = m.profile("y", subtract_min=False) assert_allclose(func0(m.values[0], y), v) v2 = m.profile("y", subtract_min=True)[1] assert np.min(v2) == 0 assert_allclose(v - np.min(v), v2) def test_profile_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() y, v = m.profile("y", grid=np.linspace(0, 4, 15)) assert len(y) == 15 assert y[0] == 0 assert y[-1] == 4 assert_allclose(func0(m.values[0], y), v) def test_profile_bad_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() with pytest.raises(ValueError): m.profile("y", grid=[[1, 2, 3]]) with pytest.raises(ValueError): m.profile("y", grid=10) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_mnprofile(grad): m = Minuit(func0, grad=grad, x=1.0, y=2.0) m.migrad() with pytest.raises(ValueError): m.mnprofile("foo") y, v, _ = m.mnprofile("y", size=10, subtract_min=False) m2 = Minuit(func0, grad=grad, x=1.0, y=2.0) m2.fixed[1] = True v2 = [] for yi in y: m2.values = (m.values[0], yi) m2.migrad() v2.append(m2.fval) assert_allclose(v, v2) # use 1 instead of "y" y, v3, _ = m.mnprofile(1, size=10, subtract_min=True) assert np.min(v3) == 0 assert_allclose(v - np.min(v), v3) def test_mnprofile_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() y, v, _ = m.mnprofile("y", grid=np.linspace(0, 4, 15)) assert len(y) == 15 assert y[0] == 0 assert y[-1] == 4 m2 = Minuit(func0, x=1.0, y=2.0) m2.fixed[1] = True v2 = [] for yi in y: m2.values = (m.values[0], yi) m2.migrad() v2.append(m2.fval) assert_allclose(v, v2) def test_mnprofile_bad_grid(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() with pytest.raises(ValueError): m.mnprofile("y", grid=10) with pytest.raises(ValueError): m.mnprofile("y", grid=[[10, 20]]) def test_contour_subtract(): m = Minuit(func0, x=1.0, y=2.0) m.migrad() v = m.contour("x", "y", subtract_min=False)[2] # use 0 and 1 instead "x", "y" v2 = m.contour(0, 1, subtract_min=True)[2] assert np.min(v2) == 0 assert_allclose(v - np.min(v), v2) def test_profile_array_func(): m = Minuit(Correlated(), (0, 0), name=("x", "y")) m.migrad() a = m.profile("y") b = m.profile(1) assert_equal(a, b) def test_mnprofile_array_func(): m = Minuit(Correlated(), (0, 0), name=("x", "y")) m.migrad() a = m.mnprofile("y") b = m.mnprofile(1) assert_equal(a, b) def test_mnprofile_bad_func(): m = Minuit(lambda x, y: 0, 0, 0) with pytest.warns(IMinuitWarning): m.mnprofile("x") def test_fmin_uninitialized(capsys): m = Minuit(func0, x=0, y=0) assert m.fmin is None assert m.fval is None def test_reverse_limit(): # issue 94 def f(x, y, z): return (x - 2) ** 2 + (y - 3) ** 2 + (z - 4) ** 2 with pytest.raises(ValueError): m = Minuit(f, x=0, y=0, z=0) m.limits["x"] = (3.0, 2.0) @pytest.fixture def minuit(): m = Minuit(func0, x=0, y=0) m.migrad() m.hesse() m.minos() return m def test_fcn(): m = Minuit(func0, x=0, y=0) v = m.fcn([2.0, 5.0]) assert v == func0(2.0, 5.0) def test_grad(): m = Minuit(func0, grad=func0_grad, x=0, y=0) v = m.fcn([2.0, 5.0]) g = m.grad([2.0, 5.0]) assert v == func0(2.0, 5.0) assert_equal(g, func0_grad(2.0, 5.0)) def test_values(minuit): expected = [2.0, 5.0] assert len(minuit.values) == 2 assert_allclose(minuit.values, expected, atol=4e-3) minuit.values = expected assert minuit.values == expected assert minuit.values[-1] == 5 assert minuit.values[0] == 2 assert minuit.values[1] == 5 assert minuit.values["x"] == 2 assert minuit.values["y"] == 5 assert minuit.values[:1] == [2] minuit.values[1:] = [3] assert minuit.values[:] == [2, 3] assert minuit.values[-1] == 3 minuit.values = 7 assert minuit.values[:] == [7, 7] with pytest.raises(KeyError): minuit.values["z"] with pytest.raises(IndexError): minuit.values[3] with pytest.raises(IndexError): minuit.values[-10] = 1 with pytest.raises(ValueError): minuit.values[:] = [2] def test_fmin(): m = Minuit(lambda x, s: (x * s) ** 2, x=1, s=1) m.fixed["s"] = True m.migrad() fm1 = m.fmin assert fm1.is_valid m.values["s"] = 0 m.migrad() fm2 = m.fmin assert fm1.is_valid assert not fm2.is_valid def test_chi2_fit(): def chi2(x, y): return (x - 1) ** 2 + ((y - 2) / 3) ** 2 m = Minuit(chi2, x=0, y=0) m.migrad() assert_allclose(m.values, (1, 2)) assert_allclose(m.errors, (1, 3)) def test_likelihood(): # normal distributed # fmt: off z = np.array([-0.44712856, 1.2245077 , 0.40349164, 0.59357852, -1.09491185, 0.16938243, 0.74055645, -0.9537006 , -0.26621851, 0.03261455, -1.37311732, 0.31515939, 0.84616065, -0.85951594, 0.35054598, -1.31228341, -0.03869551, -1.61577235, 1.12141771, 0.40890054, -0.02461696, -0.77516162, 1.27375593, 1.96710175, -1.85798186, 1.23616403, 1.62765075, 0.3380117 , -1.19926803, 0.86334532, -0.1809203 , -0.60392063, -1.23005814, 0.5505375 , 0.79280687, -0.62353073, 0.52057634, -1.14434139, 0.80186103, 0.0465673 , -0.18656977, -0.10174587, 0.86888616, 0.75041164, 0.52946532, 0.13770121, 0.07782113, 0.61838026, 0.23249456, 0.68255141, -0.31011677, -2.43483776, 1.0388246 , 2.18697965, 0.44136444, -0.10015523, -0.13644474, -0.11905419, 0.01740941, -1.12201873, -0.51709446, -0.99702683, 0.24879916, -0.29664115, 0.49521132, -0.17470316, 0.98633519, 0.2135339 , 2.19069973, -1.89636092, -0.64691669, 0.90148689, 2.52832571, -0.24863478, 0.04366899, -0.22631424, 1.33145711, -0.28730786, 0.68006984, -0.3198016 , -1.27255876, 0.31354772, 0.50318481, 1.29322588, -0.11044703, -0.61736206, 0.5627611 , 0.24073709, 0.28066508, -0.0731127 , 1.16033857, 0.36949272, 1.90465871, 1.1110567 , 0.6590498 , -1.62743834, 0.60231928, 0.4202822 , 0.81095167, 1.04444209]) # fmt: on data = 2 * z + 1 def nll(mu, sigma): z = (data - mu) / sigma logp = -0.5 * z**2 - np.log(sigma) return -np.sum(logp) m = Minuit(nll, mu=0, sigma=1) m.errordef = Minuit.LIKELIHOOD m.limits["sigma"] = (0, None) m.migrad() mu = np.mean(data) sigma = np.std(data) assert_allclose(m.values, (mu, sigma), rtol=5e-3) s_mu = sigma / len(data) ** 0.5 assert_allclose(m.errors, (s_mu, 0.12047), rtol=1e-1) def test_oneside(): # Solution: x=2., y=5. m = Minuit(func0, x=0, y=0) m.limits["x"] = (None, 9) m.migrad() assert_allclose(m.values, (2, 5), atol=2e-2) m.values["x"] = 0 m.limits["x"] = (None, 1) m.migrad() assert_allclose(m.values, (1, 5), atol=1e-3) m.values = (5, 0) m.limits["x"] = (3, None) m.migrad() assert_allclose(m.values, (3, 5), atol=4e-3) def test_oneside_outside(): m = Minuit(func0, x=5, y=0) m.limits["x"] = (None, 1) assert m.values["x"] == 1 m.limits["x"] = (2, None) assert m.values["x"] == 2 def test_migrad_ncall(): class Func: nfcn = 0 def __call__(self, x): self.nfcn += 1 return np.exp(x**2) # check that counting is accurate fcn = Func() m = Minuit(fcn, x=3) m.migrad() assert m.nfcn == fcn.nfcn fcn.nfcn = 0 m.reset() m.migrad() assert m.nfcn == fcn.nfcn ncalls_without_limit = m.nfcn # check that ncall argument limits function calls in migrad # note1: Minuit only checks the ncall counter in units of one iteration # step, therefore the call counter is in general not equal to ncall. # note2: If you pass ncall=0, Minuit uses a heuristic value that depends # on the number of parameters. m.reset() m.migrad(ncall=1) assert m.nfcn < ncalls_without_limit @pytest.mark.parametrize("arg", (1, np.array([1.0, 2.0]))) def test_ngrad(arg): class Func: ngrad = 0 def __call__(self, x): return np.sum(x**2) def grad(self, x): self.ngrad += 1 if np.ndim(x) == 1: return 2 * x return [2 * x] # check that counting is accurate fcn = Func() m = Minuit(fcn, arg) m.migrad() assert m.ngrad > 0 assert m.ngrad == fcn.ngrad fcn.ngrad = 0 m.reset() m.migrad() assert m.ngrad == fcn.ngrad # HESSE ignores analytical gradient before = m.ngrad m.hesse() assert m.ngrad == before m.reset() m.migrad() m2 = Minuit(lambda x: fcn(x), arg) m2.migrad() assert m.ngrad > 0 assert m2.ngrad == 0 # apparently this is not always the case: # assert m2.nfcn > m.nfcn def test_errordef(): m = Minuit(lambda x: x**2, 0) m.errordef = 4 assert m.errordef == 4 m.migrad() m.hesse() assert_allclose(m.errors["x"], 2) m.errordef = 1 m.hesse() assert_allclose(m.errors["x"], 1) with pytest.raises(ValueError): m.errordef = 0 def test_print_level(): from iminuit._core import MnPrint m = Minuit(lambda x: 0, x=0) m.print_level = 0 assert m.print_level == 0 assert MnPrint.global_level == 0 m.print_level = 1 assert MnPrint.global_level == 1 MnPrint.global_level = 0 def test_params(): m = Minuit(func0, x=1, y=2) m.errors = (3, 4) m.fixed["x"] = True m.limits["y"] = (None, 10) # these are the initial param states expected = ( Param(0, "x", 1.0, 3.0, None, False, True, None, None), Param(1, "y", 2.0, 4.0, None, False, False, None, 10), ) assert m.params == expected m.migrad() m.minos() assert m.init_params == expected expected = [ Namespace(number=0, name="x", value=1.0, error=3.0, merror=(-3.0, 3.0)), Namespace(number=1, name="y", value=5.0, error=1.0, merror=(-1.0, 1.0)), ] params = m.params for i, exp in enumerate(expected): p = params[i] assert p.number == exp.number assert p.name == exp.name assert p.value == approx(exp.value, rel=1e-2) assert p.error == approx(exp.error, rel=1e-2) assert p.error == approx(exp.error, rel=1e-2) def test_non_analytical_function(): class Func: i = 0 def __call__(self, a): self.i += 1 return self.i % 3 m = Minuit(Func(), 0) m.migrad() assert not m.fmin.is_valid assert m.fmin.is_above_max_edm def test_non_invertible(): m = Minuit(lambda x, y: 0, 1, 2) m.strategy = 0 m.migrad() assert m.fmin.is_valid m.hesse() assert not m.fmin.is_valid assert m.covariance is None def test_function_without_local_minimum(): m = Minuit(lambda a: -a, 0) m.migrad() assert not m.fmin.is_valid assert m.fmin.is_above_max_edm def test_function_with_maximum(): def func(a): return -(a**2) m = Minuit(func, a=0) m.migrad() assert not m.fmin.is_valid def test_perfect_correlation(): def func(a, b): return (a - b) ** 2 m = Minuit(func, a=1, b=2) m.migrad() assert m.fmin.is_valid assert not m.fmin.has_accurate_covar assert not m.fmin.has_posdef_covar assert m.fmin.has_made_posdef_covar def test_modify_param_state(): m = Minuit(func0, x=1, y=2) m.errors["y"] = 1 m.fixed["y"] = True m.migrad() assert_allclose(m.values, [2, 2], atol=1e-4) assert_allclose(m.errors, [2, 1], atol=1e-4) m.fixed["y"] = False m.values["x"] = 1 m.errors["x"] = 1 assert_allclose(m.values, [1, 2], atol=1e-4) assert_allclose(m.errors, [1, 1], atol=1e-4) m.migrad() assert_allclose(m.values, [2, 5], atol=1e-3) assert_allclose(m.errors, [2, 1], atol=1e-3) m.values["y"] = 6 m.hesse() assert_allclose(m.values, [2, 6], atol=1e-3) assert_allclose(m.errors, [2, 0.35], atol=1e-3) def test_view_lifetime(): m = Minuit(func0, x=1, y=2) val = m.values del m val["x"] = 3 # should not segfault assert val["x"] == 3 def test_hesse_without_migrad(): m = Minuit(lambda x: x**2 + x**4, x=0) m.errordef = 0.5 # second derivative: 12 x^2 + 2 m.hesse() assert m.errors["x"] == approx(0.5**0.5, abs=1e-4) m.values["x"] = 1 m.hesse() assert m.errors["x"] == approx((1.0 / 14.0) ** 0.5, abs=1e-4) assert m.fmin m = Minuit(lambda x: 0, 0) m.hesse() assert not m.accurate assert m.fmin.hesse_failed def test_edm_goal(): m = Minuit(func0, x=0, y=0) m.migrad() assert m.fmin.edm_goal == approx(0.0002) m.hesse() assert m.fmin.edm_goal == approx(0.0002) def throwing(x): raise RuntimeError("user message") def divide_by_zero(x): return 1 / 0 def returning_nan(x): return np.nan def returning_garbage(x): return "foo" @pytest.mark.parametrize( "func,expected", [ (throwing, RuntimeError("user message")), (divide_by_zero, ZeroDivisionError("division by zero")), (returning_nan, RuntimeError("result is NaN")), (returning_garbage, RuntimeError("Unable to cast Python instance")), ], ) def test_bad_functions(func, expected): m = Minuit(func, x=1) m.throw_nan = True with pytest.raises(type(expected)) as excinfo: m.migrad() assert str(expected) in str(excinfo.value) def test_throw_nan(): m = Minuit(returning_nan, x=1) assert not m.throw_nan m.migrad() m.throw_nan = True with pytest.raises(RuntimeError): m.migrad() assert m.throw_nan def returning_nan_array(x): return np.array([1, np.nan]) def returning_garbage_array(x): return np.array([1, "foo"]) def returning_noniterable(x): return 0 @pytest.mark.parametrize( "func,expected", [ (throwing, RuntimeError("user message")), (divide_by_zero, ZeroDivisionError("division by zero")), (returning_nan_array, RuntimeError("result is NaN")), (returning_garbage_array, RuntimeError("Unable to cast Python instance")), (returning_noniterable, RuntimeError()), ], ) def test_bad_functions_np(func, expected): m = Minuit(lambda x: np.dot(x, x), (1, 1), grad=func) m.throw_nan = True with pytest.raises(type(expected)) as excinfo: m.migrad() assert str(expected) in str(excinfo.value) @pytest.mark.parametrize("sign", (-1, 1)) def test_parameter_at_limit(sign): m = Minuit(lambda x: (x - sign * 1.2) ** 2, x=0) m.limits["x"] = (-1, 1) m.migrad() assert m.values["x"] == approx(sign * 1.0, abs=1e-3) assert m.fmin.has_parameters_at_limit m = Minuit(lambda x: (x - sign * 1.2) ** 2, x=0) m.migrad() assert m.values["x"] == approx(sign * 1.2, abs=1e-3) assert not m.fmin.has_parameters_at_limit @pytest.mark.parametrize("iterate,valid", ((1, False), (5, True))) def test_inaccurate_fcn(iterate, valid): def f(x): return abs(x) ** 10 + 1e7 m = Minuit(f, x=2) m.migrad(iterate=iterate) assert m.valid == valid def test_migrad_iterate(): m = Minuit(lambda x: 0, x=2) with pytest.raises(ValueError): m.migrad(iterate=0) def test_precision(): def fcn(x): return np.exp(x * x + 1) m = Minuit(fcn, x=-1) assert m.precision is None m.precision = 0.1 assert m.precision == 0.1 m.migrad() fm1 = m.fmin m.reset() m.precision = 1e-9 m.migrad() fm2 = m.fmin assert fm2.edm < fm1.edm with pytest.raises(ValueError): m.precision = -1.0 fcn.precision = 0.1 fm3 = Minuit(fcn, x=-1).migrad().fmin assert fm3.edm == fm1.edm @pytest.mark.parametrize("grad", (None, func0_grad)) def test_scan(grad): m = Minuit(func0, x=0, y=0, grad=grad) m.errors[0] = 10 m.limits[1] = (-10, 10) m.scan(ncall=99) assert m.fmin.nfcn == approx(99, rel=0.2) if grad is None: assert m.valid assert_allclose(m.values, (2, 5), atol=0.6) def test_scan_with_fixed_par(): m = Minuit(func0, x=3, y=0) m.fixed["x"] = True m.limits[1] = (-10, 10) m.scan() assert m.valid assert_allclose(m.values, (3, 5), atol=0.1) assert m.errors[1] == approx(1, abs=8e-3) m = Minuit(func0, x=5, y=4) m.fixed["y"] = True m.limits[0] = (0, 10) m.scan() assert m.valid assert_allclose(m.values, (2, 4), atol=0.1) assert m.errors[0] == approx(2, abs=1e-1) @pytest.mark.parametrize("grad", (None, func0_grad)) def test_simplex(grad): m = Minuit(func0, x=0, y=0, grad=grad) m.tol = 2e-4 # must decrease tolerance to get same accuracy as Migrad m.simplex() assert m.valid assert_allclose(m.values, (2, 5), atol=5e-3) m2 = Minuit(func0, x=0, y=0, grad=grad) m2.precision = 0.001 m2.simplex() assert m2.fval != m.fval m3 = Minuit(func0, x=0, y=0, grad=grad) m3.simplex(ncall=10) assert 10 <= m3.fmin.nfcn < 15 assert m3.fval > m.fval def test_simplex_with_fixed_par_and_limits(): m = Minuit(func0, x=3, y=0) m.tol = 2e-4 # must decrease tolerance to get same accuracy as Migrad m.fixed["x"] = True m.limits[1] = (-10, 10) m.simplex() assert m.valid assert_allclose(m.values, (3, 5), atol=2e-3) m = Minuit(func0, x=5, y=4) m.tol = 2e-4 # must decrease tolerance to get same accuracy as Migrad m.fixed["y"] = True m.limits[0] = (0, 10) m.simplex() assert m.valid assert_allclose(m.values, (2, 4), atol=3e-3) def test_tolerance(): m = Minuit(func0, x=0, y=0) assert m.tol == 0.1 m.migrad() assert m.valid edm = m.fmin.edm m.tol = 0 m.reset() m.migrad() assert m.fmin.edm < edm m.reset() m.tol = None assert m.tol == 0.1 m.reset() m.migrad() assert m.fmin.edm == edm def test_bad_tolerance(): m = Minuit(func0, x=0, y=0) with pytest.raises(ValueError): m.tol = -1 def test_cfunc(): nb = pytest.importorskip("numba") c_sig = nb.types.double(nb.types.uintc, nb.types.CPointer(nb.types.double)) @nb.cfunc(c_sig) def fcn(n, x): x = nb.carray(x, (n,)) r = 0.0 for i in range(n): r += (x[i] - i) ** 2 return r m = Minuit(fcn, (1, 2, 3)) m.migrad() assert_allclose(m.values, (0, 1, 2), atol=1e-8) @pytest.mark.parametrize("cl", (0.5, None, 0.9)) @pytest.mark.parametrize("experimental", (False, True)) def test_confidence_level(cl, experimental): stats = pytest.importorskip("scipy.stats") mpath = pytest.importorskip("matplotlib.path") cov = ((1.0, 0.5), (0.5, 4.0)) truth = (1.0, 2.0) d = stats.multivariate_normal(truth, cov) def nll(par): return -np.log(d.pdf(par)) nll.errordef = 0.5 cl_ref = 0.68 if cl is None else cl m = Minuit(nll, (0.0, 0.0)) m.migrad() n = 10000 r = d.rvs(n, random_state=1) # check that mncontour indeed contains fraction of random points equal to CL pts = m.mncontour("x0", "x1", cl=cl, experimental=experimental) p = mpath.Path(pts) cl2 = np.sum(p.contains_points(r)) / n assert cl2 == approx(cl_ref, abs=0.01) # check that minos interval indeed contains fraction of random points equal to CL m.minos(cl=cl) for ipar, (v, me) in enumerate(zip(m.values, m.merrors.values())): a = v + me.lower b = v + me.upper cl2 = np.sum((a < r[:, ipar]) & (r[:, ipar] < b)) / n assert cl2 == approx(cl_ref, abs=0.01) def test_repr(): m = Minuit(func0, 0, 0) assert repr(m) == f"{m.params!r}" m.migrad() assert repr(m) == f"{m.fmin!r}\n{m.params!r}\n{m.covariance!r}" m.minos() assert repr(m) == f"{m.fmin!r}\n{m.params!r}\n{m.merrors!r}\n{m.covariance!r}" @pytest.mark.parametrize("grad", (None, func0_grad)) def test_pickle(grad): import pickle m = Minuit(func0, x=1, y=1, grad=grad) m.fixed[1] = True m.limits[0] = 0, 10 m.migrad() pkl = pickle.dumps(m) m2 = pickle.loads(pkl) assert id(m2) != id(m) # check correct linking of views assert id(m2.values._minuit) == id(m2) assert id(m2.errors._minuit) == id(m2) assert id(m2.limits._minuit) == id(m2) assert id(m2.fixed._minuit) == id(m2) assert m2.init_params == m.init_params assert m2.params == m.params assert m2.fmin == m.fmin assert_equal(m2.covariance, m.covariance) m.fixed = False m2.fixed = False m.migrad() m.minos() m2.migrad() m2.minos() assert m2.merrors == m.merrors assert m2.fmin.fval == m.fmin.fval assert m2.fmin.edm == m.fmin.edm assert m2.fmin.nfcn == m.fmin.nfcn assert m2.fmin.ngrad == m.fmin.ngrad def test_minos_new_min(): xref = [1.0] m = Minuit(lambda x: (x - xref[0]) ** 2, x=0) m.migrad() assert m.values[0] == approx(xref[0], abs=1e-3) m.minos() assert m.merrors["x"].lower == approx(-1, abs=1e-2) assert m.merrors["x"].upper == approx(1, abs=1e-2) xref[0] = 1.1 m.minos() # values are not updated... assert m.values[0] == approx(1.0, abs=1e-3) # should be 1.1 # ...but interval is correct assert m.merrors["x"].lower == approx(-0.9, abs=1e-2) assert m.merrors["x"].upper == approx(1.1, abs=1e-2) def test_minos_without_migrad(): m = Minuit(lambda x, y: (x - 1) ** 2 + (y / 2) ** 2, 1.001, 0.001) m.minos() me = m.merrors["x"] assert me.is_valid assert me.lower == approx(-1, abs=5e-3) assert me.upper == approx(1, abs=5e-3) me = m.merrors["y"] assert me.is_valid assert me.lower == approx(-2, abs=5e-3) assert me.upper == approx(2, abs=5e-3) def test_missing_ndata(): m = Minuit(lambda a: a, 1) assert_equal(m.ndof, np.nan) def test_call_limit_reached_in_hesse(): m = Minuit(lambda x: ((x - 1.2) ** 4).sum(), np.ones(10) * 10) m.migrad(ncall=200) assert m.fmin.has_reached_call_limit assert m.fmin.nfcn < 205 def test_bad_cl(): m = Minuit(func0, 1, 1) m.migrad() for cl in (0, -1): with pytest.raises(ValueError): m.minos(cl=cl) with pytest.raises(ValueError): m.mncontour("x", "y", cl=cl) def test_negative_errors(): m = Minuit(func0, -1, -1) assert np.all(np.array(m.errors) > 0) with pytest.warns(): m.errors[0] = -1 assert m.errors[0] > 0 with pytest.warns(): m.errors = -2 assert np.all(np.array(m.errors) > 0) m.errors = 10 assert_allclose(m.errors, 10) m.errors = (1, 2) assert_allclose(m.errors, (1, 2)) def test_visualize(): m = Minuit(func0, 1, 1) m.migrad() with pytest.raises(AttributeError): m.visualize() kwargs = {} func0.visualize = lambda args, **kw: kwargs.update(kw) m.visualize(foo="bar") assert kwargs == {"foo": "bar"} del func0.visualize def test_annotated_cost_function(): def cost(a, b: Annotated[float, 0.1:1]): return a**2 + b**2 m = Minuit(cost, 0.5, 0.5) assert m.limits[0] == (-np.inf, np.inf) assert m.limits[1] == (0.1, 1.0) m.migrad() assert_allclose(m.values, (0, 0.1), atol=1e-2) m2 = Minuit(cost, 0.5, 0.5, name=("x", "y")) assert m2.limits["x"] == (-np.inf, np.inf) assert m2.limits["y"] == (0.1, 1.0) m.migrad() assert_allclose(m.values, (0, 0.1), atol=1e-2) iminuit-2.24.0/tests/test_pdg_format.py0000644000000000000000000002356714332717401015117 0ustar00# Copyright 2020 Hans Dembinski # Redistribution and use in source and binary forms, with or without modification, are # permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this list of # conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, this list # of conditions and the following disclaimer in the documentation and/or other materials # provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. # test: pytest --doctest-modules coverage report: pytest --cov --cov-report html import pytest from iminuit.pdg_format import ( _find_smallest_nonzero_abs_value, _is_asym, _unpack, _strip, _unpacked_index, pdg_format, term, latex, ) import math def test_find_smallest_nonzero_abs_value(): assert _find_smallest_nonzero_abs_value((-1.1, 3)) == (0, 1.1) assert _find_smallest_nonzero_abs_value((-10, 3, 1.2, 0, 4)) == (2, 1.2) assert _find_smallest_nonzero_abs_value(()) == (None, math.inf) assert _find_smallest_nonzero_abs_value((0, math.inf, -math.inf)) == ( None, math.inf, ) def test_is_asym(): assert _is_asym((1, 2)) assert not _is_asym(1) assert _is_asym([1, 2]) with pytest.raises(ValueError): _is_asym([]) with pytest.raises(ValueError): _is_asym([1, 2, 3]) def test_unpack(): assert _unpack([1, (2, 3), 4]) == [1, -2, 3, 4] with pytest.raises(ValueError): _unpack([1, [2, 3, 4]]) def test_unpacked_index(): assert _unpacked_index([1, 1], 0) == 0 assert _unpacked_index([1, 1], 1) == 1 assert _unpacked_index([1, 1], 2) == 2 assert _unpacked_index([1, -1, 1], 0) == 0 assert _unpacked_index([1, -1, 1], 1) == 1 assert _unpacked_index([1, -1, 1], 2) == 3 assert _unpacked_index([1, -1, 1, 1], 3) == 4 assert _unpacked_index([1, -1, 1, -1, 1], 3) == 5 def test_strip(): assert _strip(["123", "20"]) == ["123", "20"] assert _strip(["10", "20"]) == ["10", "20"] assert _strip(["0.10", "0.20", "0.30"]) == ["0.1", "0.2", "0.3"] assert _strip(["0.11", "0.20"]) == ["0.11", "0.20"] assert _strip(["10.0", "20.0"]) == ["10", "20"] assert _strip(["1.200", "3.40"]) == ["1.20", "3.4"] assert _strip(["0.000", "0.000"]) == ["0", "0"] assert _strip(["-0.00", "0.00"]) == ["0", "0"] def test_term_format(): def ft(*args, **kwargs): return pdg_format(*args, format=term, **kwargs) assert ft(2.345e-09, 0.1e-09) == "(2.35 ± 0.10)E-09" assert ft(2.345e-09, 0.354e-09) == "(2.35 ± 0.35)E-09" assert ft(2.345e-09, 0.355e-09) == "(2.3 ± 0.4)E-09" assert ft(2.345e-09, 0.949e-09) == "(2.3 ± 0.9)E-09" assert ft(2.345e-09, 0.95e-09) == "(2.3 ± 1.0)E-09" assert ft(2.345e-09, 0.999e-09) == "(2.3 ± 1.0)E-09" assert ft(2.3456, 0.001) == "2.3456 ± 0.0010" assert ft(2.3456, 0.00354) == "2.3456 ± 0.0035" assert ft(2.3456, 0.00355) == "2.346 ± 0.004" assert ft(2.3456, 0.00949) == "2.346 ± 0.009" assert ft(2.3456, 0.0095) == "2.346 ± 0.010" assert ft(2.3456, 0.00999) == "2.346 ± 0.010" assert ft(2.3456, 0.01) == "2.346 ± 0.010" assert ft(2.3456, 0.0354) == "2.346 ± 0.035" assert ft(2.3456, 0.0355) == "2.35 ± 0.04" assert ft(2.3456, 0.0949) == "2.35 ± 0.09" assert ft(2.3456, 0.095) == "2.35 ± 0.10" assert ft(2.3456, 0.0999) == "2.35 ± 0.10" assert ft(2.3456, 0.1) == "2.35 ± 0.10" assert ft(2.3456, 0.354) == "2.35 ± 0.35" assert ft(2.3456, 0.355) == "2.3 ± 0.4" assert ft(2.3456, 0.949) == "2.3 ± 0.9" assert ft(2.3456, 0.95) == "2.3 ± 1.0" assert ft(2.3456, 0.999) == "2.3 ± 1.0" assert ft(2.3456, 1) == "2.3 ± 1.0" assert ft(2.3456, 3.54) == "2.3 ± 3.5" assert ft(2.3456, 3.55) == "2 ± 4" assert ft(2.3456, 9.49) == "2 ± 9" assert ft(2.3456, 9.5) == "2 ± 10" assert ft(2.3456, 9.99) == "2 ± 10" assert ft(23.456, 10) == "23 ± 10" assert ft(23.456, 35.4) == "23 ± 35" assert ft(23.456, 35.5) == "20 ± 40" assert ft(23.456, 94.9) == "20 ± 90" assert ft(23.456, 95.0) == "20 ± 100" assert ft(23.456, 99.9) == "20 ± 100" assert ft(234.56, 100) == "(0.23 ± 0.10)E+03" assert ft(234.56, 354) == "(0.23 ± 0.35)E+03" assert ft(234.56, 355) == "(0.2 ± 0.4)E+03" assert ft(234.56, 949) == "(0.2 ± 0.9)E+03" assert ft(234.56, 950) == "(0.2 ± 1.0)E+03" assert ft(234.56, 999) == "(0.2 ± 1.0)E+03" assert ft(2345.6, 1000) == "(2.3 ± 1.0)E+03" assert ft(2345.6, 3540) == "(2.3 ± 3.5)E+03" assert ft(2345.6, 3550) == "(2 ± 4)E+03" assert ft(2345.6, 9490) == "(2 ± 9)E+03" assert ft(2345.6, 9500) == "(2 ± 10)E+03" assert ft(2345.6, 9990) == "(2 ± 10)E+03" assert ft(2.3456e12, 1e11) == "(2.35 ± 0.10)E+12" assert ft(2.3456e12, 3.54e11) == "(2.35 ± 0.35)E+12" assert ft(2.3456e12, 3.55e11) == "(2.3 ± 0.4)E+12" assert ft(2.3456e12, 9.49e11) == "(2.3 ± 0.9)E+12" assert ft(2.3456e12, 9.5e11) == "(2.3 ± 1.0)E+12" assert ft(2.3456e12, 9.99e11) == "(2.3 ± 1.0)E+12" assert ft(-2.3456e13, 1e11) == "(-23.46 ± 0.10)E+12" assert ft(-2.3456e13, 3.54e11) == "(-23.46 ± 0.35)E+12" assert ft(-2.3456e13, 3.55e11) == "(-23.5 ± 0.4)E+12" assert ft(-2.3456e13, 9.49e11) == "(-23.5 ± 0.9)E+12" assert ft(-2.3456e13, 9.5e11) == "(-23.5 ± 1.0)E+12" assert ft(-2.3456e13, 9.99e11) == "(-23.5 ± 1.0)E+12" assert ft(math.nan, 1.0) == "nan ± 1" assert ft(math.nan, 3.54) == "nan ± 3.5" assert ft(math.nan, 3.55) == "nan ± 4" assert ft(math.nan, 9.49) == "nan ± 9" assert ft(math.nan, 9.99) == "nan ± 10" assert ft(math.inf, 1.0) == "inf ± 1" assert ft(math.inf, 3.54) == "inf ± 3.5" assert ft(-math.inf, 3.55) == "-inf ± 4" assert ft(math.inf, 9.49) == "inf ± 9" assert ft(-math.inf, 9.99) == "-inf ± 10" assert ft(math.nan, 1.0e3) == "(nan ± 1)E+03" assert ft(math.nan, 3.54e3) == "(nan ± 3.5)E+03" assert ft(math.nan, 3.55e3) == "(nan ± 4)E+03" assert ft(math.nan, 9.49e3) == "(nan ± 9)E+03" assert ft(math.nan, 9.99e3) == "(nan ± 10)E+03" assert ft(2.3456, math.nan) == "2.3456 ± nan" assert ft(1.2345e9, math.nan) == "(1.2345 ± nan)E+09" assert ft(2.3456e4, math.inf) == "(2.3456 ± inf)E+04" # implementation is robust against input errors assert ft(2.3456e-3, -1) == "0 -1" assert ft(0, 0) == "0 ± 0" assert ft(2.3456e10, 0) == "(2.3456 ± 0.0000)E+10" assert ft(2.3456e10, 0) == "(2.3456 ± 0.0000)E+10" assert ft(1.2345e100, 1.2345e100) == "(0.012 ± 0.012)E+102" assert ft(1.2345, 0.123, 0.0123) == "1.234 ± 0.123 ± 0.012" assert ft(1.2345, 0.1, 0.4) == "1.23 ± 0.10 ± 0.40" assert ft(1.234, (0.11, 0.22), 0.45) == "1.23 -0.11 +0.22 ± 0.45" assert ( ft(1.234, -0.11, 0.22, 0.45, labels=("a", "b")) == "1.23 -0.11 +0.22 (a) ± 0.45 (b)" ) assert ( ft(1.234, -0.11, 0.22, 0.45, labels=("a", "b"), leader=1) == "1.2 -0.1 +0.2 (a) ± 0.5 (b)" ) data_as_list = [1.234, -0.11, 0.22, 0.45] assert ft(*data_as_list, leader=1) == "1.2 -0.1 +0.2 ± 0.5" assert ft(1.2, -0.0, 0.0, 0.0, leader=0) == "1.2 -0.0 +0.0 ± 0.0" assert ft(-1.234567e-22, 1.234567e-11) == "(-0.000 ± 0.012)E-09" def test_latex_format(): def ft(*args, **kwargs): return pdg_format(*args, format=latex, **kwargs) nan = math.nan inf = math.inf assert ft(234.56, 12.3) == r"235 \pm 12" assert ft(2345.6, 123) == r"(2.35 \pm 0.12) \times 10^{3}" assert ft(2345.6, 355) == r"(2.3 \pm 0.4) \times 10^{3}" assert ft(234.5, 123.4) == r"(0.23 \pm 0.12) \times 10^{3}" assert ft(23.45, 12.34) == r"23 \pm 12" assert ft(2.345, 1.234) == r"2.3 \pm 1.2" assert ft(0.2345, 0.1234) == r"0.23 \pm 0.12" assert ft(0.02345, 0.01234) == r"0.023 \pm 0.012" assert ft(0.02345, 0.09123) == r"0.02 \pm 0.09" assert ft(nan, 2.34e3) == r"(\mathrm{NaN} \pm 2.3) \times 10^{3}" assert ft(1e9, nan) == r"(1 \pm \mathrm{NaN}) \times 10^{9}" assert ft(inf, 2.345e3) == r"(\infty \pm 2.3) \times 10^{3}" assert ft(1.2345e9, inf) == r"(1.2345 \pm \infty) \times 10^{9}" assert ft(inf, -inf) == r"\infty \pm \infty" # tolerance against input errors assert ft(0, 0) == r"0 \pm 0" assert ft(1.234, 0.123, 2.345) == r"1.23 \pm 0.12 \pm 2.35" assert ft(1.234, 0.96, 0.45) == r"1.2 \pm 1.0 \pm 0.5" assert ( ft(1.234, (0.12, 0.78), (0.045, 0.067)) == r"1.23 {}_{-0.12}^{+0.78} {}_{-0.05}^{+0.07}" ) assert ( ft(1.234, (0.12, 0.78), (0.045, 0.067)) == r"1.23 {}_{-0.12}^{+0.78} {}_{-0.05}^{+0.07}" ) assert ( ft(1.234, (0.12, 0.78), (0.45, 0.67), leader=1) == r"1.2 {}_{-0.1}^{+0.8} {}_{-0.5}^{+0.7}" ) assert ( ft(1.234, -0.12, 0.78, -0.45, 0.67, leader=1) == r"1.2 {}_{-0.1}^{+0.8} {}_{-0.5}^{+0.7}" ) assert ( ft(1.234, 0.123, 2.345, labels=(r"_\mathrm{a}", "b")) == r"1.23 \pm 0.12_\mathrm{a} \pm 2.35 (\mathrm{b})" ) assert ( ft(1.234, (0.123, 2.345), 4.56, labels=("a", "b")) == r"1.23 {}_{-0.12}^{+2.35} (\mathrm{a}) \pm 4.56 (\mathrm{b})" ) iminuit-2.24.0/tests/test_repr.py0000644000000000000000000003104014332717401013726 0ustar00# flake8: noqa: E501 from iminuit import Minuit from iminuit.util import Param, Matrix, FMin, MError from iminuit import _repr_html, _repr_text import pytest from argparse import Namespace from pathlib import Path import numpy as np from iminuit._hide_modules import hide_modules nan = float("nan") inf = float("infinity") def f1(x, y): return (x - 2) ** 2 + (y - 1) ** 2 / 0.25 + 1 def test_color_1(): g = _repr_html.ColorGradient((-1, 10, 10, 20), (2, 20, 20, 10)) assert g.rgb(-1) == "rgb(10,10,20)" assert g.rgb(2) == "rgb(20,20,10)" assert g.rgb(-1.00001) == "rgb(10,10,20)" assert g.rgb(1.99999) == "rgb(20,20,10)" assert g.rgb(0.5) == "rgb(15,15,15)" def test_color_2(): g = _repr_html.ColorGradient( (-1, 50, 50, 250), (0, 100, 100, 100), (1, 250, 50, 50) ) assert g.rgb(-1) == "rgb(50,50,250)" assert g.rgb(-0.5) == "rgb(75,75,175)" assert g.rgb(0) == "rgb(100,100,100)" assert g.rgb(0.5) == "rgb(175,75,75)" assert g.rgb(1) == "rgb(250,50,50)" def test_html_tag(): tag = _repr_html.tag def stag(*args, **kwargs): return _repr_html.to_str(tag(*args, **kwargs)) # fmt: off assert stag('foo', 'bar', baz='hi', xyzzy='2') == ' bar ' assert stag('foo') == """""" assert tag('foo', tag('bar', 'baz')) == ['', [' baz '], ''] assert stag('foo', tag('bar', 'baz')) == """ baz """ # fmt: on def ref(fn): with open(Path(__file__).parent / fn, encoding="utf-8") as f: return f.read().strip() def test_pdg_format(): assert _repr_text.pdg_format(1.2567, 0.1234) == ["1.26", "0.12"] assert _repr_text.pdg_format(1.2567e3, 0.1234e3) == ["1.26e3", "0.12e3"] assert _repr_text.pdg_format(1.2567e4, 0.1234e4) == ["12.6e3", "1.2e3"] assert _repr_text.pdg_format(1.2567e-1, 0.1234e-1) == ["0.126", "0.012"] assert _repr_text.pdg_format(1.2567e-2, 0.1234e-2) == ["0.0126", "0.0012"] assert _repr_text.pdg_format(1.0, 0.0, 0.25) == ["1.00", "0.00", "0.25"] assert _repr_text.pdg_format(0, 1, -1) == ["0", "1", "-1"] assert _repr_text.pdg_format(2, -1, 1) == ["2", "-1", "1"] assert _repr_text.pdg_format(2.01, -1.01, 1.01) == ["2", "-1", "1"] assert _repr_text.pdg_format(1.999, -0.999, 0.999) == ["2", "-1", "1"] assert _repr_text.pdg_format(1, 0.5, -0.5) == ["1.0", "0.5", "-0.5"] assert _repr_text.pdg_format(1.0, 1e-3) == ["1.000", "0.001"] assert _repr_text.pdg_format(1.0, 1e-4) == ["1.0000", "0.0001"] assert _repr_text.pdg_format(1.0, 1e-5) == ["1.00000", "0.00001"] assert _repr_text.pdg_format(-1.234567e-22, 1.234567e-11) == ["-0", "0.012e-9"] assert _repr_text.pdg_format(nan, 1.23e-2) == ["nan", "0.012"] assert _repr_text.pdg_format(nan, 1.23e10) == ["nan", "0.012e12"] assert _repr_text.pdg_format(nan, -nan) == ["nan", "nan"] assert _repr_text.pdg_format(inf, 1.23e10) == ["inf", "0.012e12"] def test_matrix_format(): def a(*args): return np.reshape(args, (2, 2)) assert _repr_text.matrix_format(a(1e1, 2e2, -3e3, -4e4)) == [ "10", "200", "-3.00e3", "-4e+04", ] assert _repr_text.matrix_format(a(2e2, nan, -nan, -4e4)) == [ "200", "nan", "nan", "-4e+04", ] assert _repr_text.matrix_format(a(2e2, inf, -nan, -4e4)) == [ "200", "inf", "nan", "-4e+04", ] @pytest.fixture def minuit(): m = Minuit(f1, x=0, y=0) m.tol = 1e-4 m.migrad() m.hesse() m.minos() return m @pytest.fixture def fmin_good(): fm = Namespace( fval=11.456, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=True, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) return FMin(fm, "Migrad", 10, 3, 10, 1e-4, 0.01) @pytest.fixture def fmin_bad(): fm = Namespace( fval=nan, edm=1.23456e-10, up=0.5, is_valid=False, has_valid_parameters=False, has_accurate_covar=False, has_posdef_covar=False, has_made_posdef_covar=True, hesse_failed=True, has_covariance=False, is_above_max_edm=True, has_reached_call_limit=True, has_parameters_at_limit=True, errordef=1, state=[ Namespace( has_limits=True, is_fixed=False, value=0, error=0.5, lower_limit=0, upper_limit=1, has_lower_limit=True, has_upper_limit=True, ) ], ) return FMin(fm, "SciPy[L-BFGS-B]", 100000, 200000, 1, 1e-5, 1.2) @pytest.fixture def fmin_no_cov(): fm = Namespace( fval=11.456, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=False, has_posdef_covar=False, has_made_posdef_covar=False, hesse_failed=False, has_covariance=False, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) return FMin(fm, "Migrad", 10, 3, 10, 1e-4, 0.01) def test_html_fmin_good(fmin_good): assert fmin_good._repr_html_() == ref("fmin_good.html").format( good=_repr_html.good_style ) def test_html_fmin_bad(fmin_bad): assert fmin_bad._repr_html_() == ref("fmin_bad.html").format( bad=_repr_html.bad_style, warn=_repr_html.warn_style ) def test_html_fmin_no_cov(fmin_no_cov): assert fmin_no_cov._repr_html_() == ref("fmin_no_cov.html").format( good=_repr_html.good_style, bad=_repr_html.bad_style, warn=_repr_html.warn_style ) def test_html_params(minuit): assert minuit.init_params._repr_html_() == ref("params_init.html") assert minuit.params._repr_html_() == ref("params.html") def test_html_params_with_limits(): m = Minuit(f1, x=3, y=5) m.fixed["x"] = True m.errors = (0.2, 0.1) m.limits = ((0, None), (0, 10)) assert m.init_params._repr_html_() == ref("params_with_limits.html") def test_html_merror(minuit): me = minuit.merrors[0] assert me._repr_html_() == ref("merror.html").format(good=_repr_html.good_style) def test_html_merrors(minuit): mes = minuit.merrors assert mes._repr_html_() == ref("merrors.html").format(good=_repr_html.good_style) def test_html_matrix(): matrix = Matrix(("x", "y")) matrix[:] = ((1.0, 0.0), (0.0, 0.25)) assert matrix._repr_html_() == ref("matrix.html") def test_html_correlation_matrix(): matrix = Matrix(("x", "y")) matrix[:] = ((1.0, 0.707), (0.707, 1.0)) assert matrix._repr_html_() == ref("matrix_2.html") def test_html_minuit(): m = Minuit(lambda x, y: x**2 + 4 * y**2, x=0, y=0) assert m._repr_html_() == m.params._repr_html_() m.migrad() assert ( m._repr_html_() == m.fmin._repr_html_() + m.params._repr_html_() + m.covariance._repr_html_() ) m.minos() assert ( m._repr_html_() == m.fmin._repr_html_() + m.params._repr_html_() + m.merrors._repr_html_() + m.covariance._repr_html_() ) def test_text_fmin_good(fmin_good): assert _repr_text.fmin(fmin_good) == ref("fmin_good.txt") def test_text_fmin_bad(fmin_bad): assert _repr_text.fmin(fmin_bad) == ref("fmin_bad.txt") def test_text_fmin_no_cov(fmin_no_cov): assert _repr_text.fmin(fmin_no_cov) == ref("fmin_no_cov.txt") def test_text_fmin_not_posdef(): fm = Namespace( fval=11.456, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=False, has_posdef_covar=False, has_made_posdef_covar=False, hesse_failed=True, has_covariance=False, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) fmin = FMin(fm, "Migrad", 10, 3, 10, 1e-4, 0.01) assert _repr_text.fmin(fmin) == ref("fmin_not_posdef.txt") def test_text_fmin_made_posdef(): fm = Namespace( fval=11.456, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=False, has_posdef_covar=True, has_made_posdef_covar=True, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) fmin = FMin(fm, "Migrad", 10, 3, 10, 1e-4, 0.01) assert _repr_text.fmin(fmin) == ref("fmin_made_posdef.txt") def test_text_fmin_approximate(): fm = Namespace( fval=11.456, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=False, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) fmin = FMin(fm, "Migrad", 10, 3, 10, 1e-4, 0.01) assert _repr_text.fmin(fmin) == ref("fmin_approximate.txt") def test_text_params(minuit): assert _repr_text.params(minuit.params) == ref("params.txt") def test_text_params_with_latex_names(): pytest.importorskip("unicodeitplus") m = Minuit(lambda x: x**2, 1, name=[r"$\alpha$"]) assert _repr_text.params(m.params) == ref("params_latex_1.txt") with hide_modules("unicodeitplus"): from iminuit.warnings import OptionalDependencyWarning with pytest.warns( OptionalDependencyWarning, match="rendering simple LaTeX requires optional package 'unicodeitplus'", ): assert _repr_text.params(m.params) == ref("params_latex_2.txt") def test_text_params_with_long_names(): mps = [ Param( 0, "super-long-name", 0, 0, None, False, False, None, None, ) ] assert _repr_text.params(mps) == ref("params_long_names.txt") def test_text_params_difficult_values(): mps = [ Param( 0, "x", -1.234567e-22, 1.234567e-11, None, True, False, None, None, ) ] assert _repr_text.params(mps) == ref("params_difficult_values.txt") def test_text_params_with_limits(): m = Minuit( f1, x=3, y=5, ) m.fixed["x"] = True m.errors = (0.2, 0.1) m.limits = ((0, None), (0, 10)) assert _repr_text.params(m.params) == ref("params_with_limits.txt") def test_text_merror(): me = MError( 0, "x", -1.0, 1.0, True, True, True, False, False, False, False, False, False, 42, 0.123, ) assert _repr_text.merrors({None: me}) == ref("merror.txt") def test_text_merrors(minuit): assert _repr_text.merrors(minuit.merrors) == ref("merrors.txt") def test_text_matrix(): m = Matrix({"x": 0, "y": 1}) m[:] = ((1.0, -0.0), (-0.0, 0.25)) assert _repr_text.matrix(m) == ref("matrix.txt") def test_text_matrix_mini(): m = Matrix({"x": 0}) m[:] = [1.0] assert _repr_text.matrix(m) == ref("matrix_mini.txt") def test_text_matrix_large(): m = Matrix({"x": 0, "y": 1, "z": 2}) m[:] = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) assert _repr_text.matrix(m) == ref("matrix_large.txt") def test_text_matrix_with_long_names(): m = Matrix({"super-long-name": 0, "x": 1}) m[:] = ((1.0, 0.1), (0.1, 1.0)) assert _repr_text.matrix(m) == ref("matrix_long_names.txt") def test_text_matrix_difficult_values(): m = Matrix({"x": 0, "y": 1}) m[:] = ((-1.23456, 0), (0, 0)) assert _repr_text.matrix(m) == ref("matrix_difficult_values.txt") def test_text_minuit(): m = Minuit(lambda x, y: x**2 + 4 * y**2, x=0, y=0) assert str(m) == str(m.params) m.migrad() assert str(m) == str(m.fmin) + "\n" + str(m.params) + "\n" + str(m.covariance) m.minos() assert str(m) == str(m.fmin) + "\n" + str(m.params) + "\n" + str( m.merrors ) + "\n" + str(m.covariance) iminuit-2.24.0/tests/test_repr_pretty.py0000644000000000000000000000742514332717401015347 0ustar00from contextlib import contextmanager from iminuit.testing import sphere_np from iminuit import util, _repr_text as tx, Minuit from argparse import Namespace class PrintAssert: data = "" def __init__(self, expected): self.expected = expected def __enter__(self): return self def __exit__(self, *args): assert self.data == self.expected def text(self, arg): self.data += arg def pretty(self, arg): if hasattr(arg, "_repr_pretty_"): arg._repr_pretty_(self, False) else: self.data += repr(arg) def breakable(self): self.data += "\n" @contextmanager def group(self, n, start, stop): self.data += start yield self.data += stop def test_Minuit(): m = Minuit(sphere_np, (0, 0)) with PrintAssert("") as pr: m._repr_pretty_(pr, True) with PrintAssert(tx.params(m.params)) as pr: m._repr_pretty_(pr, False) m.migrad() expected = ( tx.fmin(m.fmin) + "\n" + tx.params(m.params) + "\n" + tx.matrix(m.covariance) ) with PrintAssert(expected) as pr: m._repr_pretty_(pr, False) m.minos() expected = ( tx.fmin(m.fmin) + "\n" + tx.params(m.params) + "\n" + tx.merrors(m.merrors) + "\n" + tx.matrix(m.covariance) ) with PrintAssert(expected) as pr: m._repr_pretty_(pr, False) def test_Matrix(): m = util.Matrix(("a", "b")) m[:] = [[1, 2], [3, 4]] with PrintAssert("") as pr: m._repr_pretty_(pr, True) with PrintAssert(tx.matrix(m)) as pr: m._repr_pretty_(pr, False) def test_Param(): values = 3, "foo", 1.2, 3.4, None, False, False, 42, None p = util.Param(*values) with PrintAssert("Param(...)") as pr: p._repr_pretty_(pr, True) with PrintAssert(tx.params([p])) as pr: p._repr_pretty_(pr, False) def test_Params(): p = util.Params([util.Param(3, "foo", 1.2, 3.4, None, False, False, 42, None)]) with PrintAssert("Params(...)") as pr: p._repr_pretty_(pr, True) with PrintAssert(tx.params(p)) as pr: p._repr_pretty_(pr, False) def test_MError(): me = util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.7, ) with PrintAssert("") as pr: me._repr_pretty_(pr, True) with PrintAssert(tx.merrors({None: me})) as pr: me._repr_pretty_(pr, False) def test_MErrors(): mes = util.MErrors( x=util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.7, ) ) with PrintAssert("") as pr: mes._repr_pretty_(pr, True) with PrintAssert(tx.merrors(mes)) as pr: mes._repr_pretty_(pr, False) def test_FMin(): fm = Namespace( fval=1.23456e-10, edm=1.23456e-10, up=0.5, is_valid=True, has_valid_parameters=True, has_accurate_covar=True, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, errordef=1, state=[], ) fmin = util.FMin(fm, "foo", 1, 2, 1, 0.1, 1.2) with PrintAssert("") as pr: fmin._repr_pretty_(pr, True) with PrintAssert(tx.fmin(fmin)) as pr: fmin._repr_pretty_(pr, False) iminuit-2.24.0/tests/test_scipy.py0000644000000000000000000001511114332717401014106 0ustar00import pytest from numpy.testing import assert_allclose from iminuit import Minuit from iminuit.testing import rosenbrock, rosenbrock_grad import numpy as np scopt = pytest.importorskip("scipy.optimize") def fcn(a, b): return a**2 + ((b - 1) / 2.0) ** 2 + 3 def grad(a, b): return 2 * a, b - 1 def hess(a, b): return [[2, 0], [0, 0.5]] def hessp(a, b, v): return np.dot(hess(a, b), v) @pytest.mark.parametrize("array_call", (False, True)) @pytest.mark.parametrize("fixed", (False, True)) @pytest.mark.parametrize( "method", ( "Nelder-Mead", "Powell", "CG", "BFGS", "Newton-CG", "L-BFGS-B", "TNC", "COBYLA", "SLSQP", "trust-constr", "dogleg", "trust-ncg", "trust-exact", "trust-krylov", ), ) def test_scipy_method(array_call, fixed, method): fn = (lambda par: fcn(*par)) if array_call else fcn gr = None he = None hep = None if method in ( "Newton-CG", "trust-constr", "dogleg", "trust-ncg", "trust-exact", "trust-krylov", ): gr = (lambda par: grad(*par)) if array_call else grad if method in ("Newton-CG", "dogleg", "trust-ncg", "trust-exact", "trust-krylov"): he = (lambda par: hess(*par)) if array_call else hess if method in ("Newton-CG", "trust-ncg", "trust-krylov", "trust-constr"): hep = (lambda par, v: hessp(*par, v)) if array_call else hessp if array_call: m = Minuit(fn, (1, 2), grad=gr) else: m = Minuit(fn, a=1, b=2, grad=gr) m.fixed[0] = fixed m.scipy(method=method, hess=he) assert m.valid if fixed: assert_allclose(m.values, [1, 1], atol=1e-3) assert_allclose(m.errors[1], 2, rtol=1e-2) else: assert_allclose(m.values, [0, 1], atol=1e-3) assert_allclose(m.errors, [1, 2], rtol=1e-2) if hep: m.scipy(method=method, hessp=hep) assert m.valid if fixed: assert_allclose(m.values, [1, 1], atol=1e-3) assert_allclose(m.errors[1], 2, rtol=1e-2) else: assert_allclose(m.values, [0, 1], atol=1e-3) assert_allclose(m.errors, [1, 2], rtol=1e-2) @pytest.mark.parametrize("stra", (0, 1)) @pytest.mark.parametrize("grad", (None, grad)) def test_scipy_unbounded(stra, grad): m = Minuit(fcn, a=1, b=2, grad=grad) m.strategy = stra m.scipy() assert m.valid assert m.accurate == (stra == 1) assert_allclose(m.values, [0, 1], atol=1e-3) if stra == 1: assert_allclose(m.errors, [1, 2], atol=3e-2) if grad: assert m.fmin.ngrad > 0 else: assert m.fmin.ngrad == 0 @pytest.mark.parametrize("stra", (0, 1)) @pytest.mark.parametrize("grad", (None, grad)) @pytest.mark.parametrize( "lower,upper", ( (-0.1, None), (0, None), (0.1, None), (None, -0.1), (None, 0), (None, 0.1), (-0.1, 0.1), ), ) def test_scipy_bounded(stra, grad, lower, upper): m = Minuit(fcn, a=1, b=2, grad=grad) m.limits["a"] = (lower, upper) m.strategy = stra m.scipy() if stra == 1: assert m.valid assert m.accurate lower = -np.inf if lower is None else lower upper = np.inf if upper is None else upper assert_allclose(m.values, [np.clip(0, lower, upper), 1], atol=1e-3) if stra == 1: assert_allclose(m.errors[1], 2, atol=3e-2) if grad: assert m.fmin.ngrad > 0 else: assert m.fmin.ngrad == 0 @pytest.mark.parametrize("grad", (None, grad)) def test_scipy_fixed(grad): m = Minuit(fcn, a=1, b=2, grad=grad) m.fixed["a"] = True m.scipy() assert m.valid assert_allclose(m.values, [1, 1], atol=1e-3) assert_allclose(m.errors, [0.01, 2], atol=3e-2) if grad: assert m.fmin.ngrad > 0 else: assert m.fmin.ngrad == 0 @pytest.mark.parametrize("stra", (0, 1)) @pytest.mark.parametrize("grad", (None, grad)) def test_scipy_errordef(stra, grad): m = Minuit(fcn, a=1, b=2, grad=grad) m.errordef = 4 m.strategy = stra m.scipy() assert m.valid assert_allclose(m.values, [0, 1], atol=1e-3) assert_allclose(m.errors, [2, 4], rtol=0.3) if grad: assert m.fmin.ngrad > 0 else: assert m.fmin.ngrad == 0 @pytest.mark.parametrize("stra", (0, 1)) @pytest.mark.parametrize("grad", (None, rosenbrock_grad)) def test_scipy_ncall(stra, grad): m = Minuit(rosenbrock, x=2, y=2, grad=grad) m.strategy = stra m.scipy() assert m.valid nfcn = m.fmin.nfcn m.reset() m.scipy(ncall=1) assert m.fmin.nfcn < nfcn assert not m.valid @pytest.mark.parametrize("lb", (0, 0.1)) @pytest.mark.parametrize("fixed", (False, True)) def test_scipy_constraints_1(lb, fixed): def fcn(a, x, y): return a + x**2 + y**2 m = Minuit(fcn, a=3, x=1, y=2) m.fixed["a"] = fixed con_a = scopt.NonlinearConstraint(lambda a, x, y: a, lb, np.inf) con_x = scopt.NonlinearConstraint(lambda a, x, y: x, lb, np.inf) con_y = scopt.NonlinearConstraint(lambda a, x, y: y, lb, np.inf) m.scipy(constraints=[con_a, con_x, con_y]) assert m.valid == (lb == 0 and fixed) if fixed: assert_allclose(m.values, [3, lb, lb], atol=1e-3) else: assert_allclose(m.values, [lb, lb, lb], atol=1e-3) assert m.accurate @pytest.mark.parametrize("fixed", (False, True)) def test_scipy_constraints_2(fixed): def fcn(x, y): return x**2 + y**2 m = Minuit(fcn, x=1, y=2) m.fixed["x"] = fixed con = scopt.LinearConstraint([1, 1], 0.1, np.inf) m.scipy(method="COBYLA", constraints=con) assert m.valid == fixed if fixed: assert_allclose(m.values, [1, 0.0], atol=1e-3) assert_allclose(m.errors[1], 1, atol=1e-3) else: assert_allclose(m.values, [0.05, 0.05], atol=1e-3) assert_allclose(m.errors, [1, 1], atol=1e-3) def test_bad_constraint(): m = Minuit(fcn, a=1, b=2) with pytest.raises(ValueError): m.scipy(constraints={}) with pytest.raises(ValueError): m.scipy(constraints=[{}]) def test_high_print_level(capsys): m = Minuit(fcn, a=1, b=2) m.scipy() assert capsys.readouterr()[0] == "" m.reset() m.print_level = 1 m.scipy() m.print_level = 0 assert capsys.readouterr()[0] != "" def test_on_modified_state(): m = Minuit(fcn, a=1, b=2) m.scipy() assert m.valid assert_allclose(m.values, [0, 1], atol=1e-3) m.fixed[1] = True # modify latest state m.values = 1, 2 m.scipy() # used to fail assert m.valid assert_allclose(m.values, [0, 2], atol=1e-3) iminuit-2.24.0/tests/test_tabulate.py0000644000000000000000000000164314332717401014565 0ustar00from iminuit import Minuit import pytest tab = pytest.importorskip("tabulate") def framed(s): return "\n" + str(s) + "\n" def test_params(): m = Minuit(lambda x, y: x**2 + (y / 2) ** 2 + 1, x=0, y=0) m.limits["y"] = (-1, 1) m.fixed["x"] = True m.migrad() m.minos() assert ( framed(tab.tabulate(*m.params.to_table())) == """ pos name value error error- error+ limit- limit+ fixed ----- ------ ------- ------- -------- -------- -------- -------- ------- 0 x 0 0.1 yes 1 y 0 1.5 -1.0 1.0 -1.0 1.0 """ ) def test_matrix(): m = Minuit(lambda x, y: x**2 + (y / 2) ** 2 + 1, x=0, y=0) m.migrad() assert ( framed(tab.tabulate(*m.covariance.to_table())) == """ x y -- --- --- x 1 0 y 0 4 """ ) iminuit-2.24.0/tests/test_util.py0000644000000000000000000004652614332717401013752 0ustar00from iminuit import util import pytest from argparse import Namespace from numpy.testing import assert_equal, assert_allclose import numpy as np from iminuit._core import MnUserParameterState from iminuit._optional_dependencies import optional_module_for import pickle from iminuit._hide_modules import hide_modules try: import scipy # noqa scipy_available = True except ModuleNotFoundError: scipy_available = False def test_ndim(): ndim = util._ndim assert ndim(1) == 0 assert ndim([]) == 1 assert ndim([[]]) == 2 assert ndim(None) == 0 assert ndim((None, None)) == 1 assert ndim(((1, 2), None)) == 2 assert ndim((None, (1, 2))) == 2 def test_BasicView(): with pytest.raises(TypeError): util.BasicView(None, 2) def test_ValueView(): state = MnUserParameterState() state.add("x", 1.0, 0.1) state.add("y", 2.2, 0.1) state.add("z", 3.3, 0.1) v = util.ValueView( Namespace( _var2pos={"x": 0, "y": 1, "z": 2}, _pos2var=("x", "y", "z"), npar=3, _last_state=state, _copy_state_if_needed=lambda: None, ) ) assert v == v assert v == (1.0, 2.2, 3.3) assert v != (1.0, 2.1, 3.3) assert v != 0 assert repr(v) == "" assert str(v) == repr(v) v[:] = (1, 2, 3) assert_equal(v[:3], (1, 2, 3)) assert_equal(v[0:3], (1, 2, 3)) assert_equal(v[0:10], (1, 2, 3)) assert_equal(v, (1, 2, 3)) v[1:] = 4 assert_equal(v, (1, 4, 4)) v["y"] = 2 assert_equal(v, (1, 2, 4)) v["y":] = 3 assert_equal(v, (1, 3, 3)) v[:"z"] = 2 assert_equal(v, (2, 2, 3)) v_dict = v.to_dict() assert isinstance(v_dict, dict) assert v_dict["x"] == v["x"] assert v_dict["y"] == v["y"] assert v_dict["z"] == v["z"] v[:] = (1, 2, 3) assert_equal(v[["x", "z"]], (1, 3)) assert_equal(v[[2, 0]], (3, 1)) v[["x", "z"]] = (3, 1) assert_equal(v, (3, 2, 1)) def test_FixedView_as_mask_for_other_views(): state = MnUserParameterState() state.add("x", 1, 0.1) state.add("y", 2, 0.1) state.add("z", 3, 0.1) fake_minuit = Namespace( _var2pos={"x": 0, "y": 1, "z": 2}, _pos2var=("x", "y", "z"), npar=3, _last_state=state, _copy_state_if_needed=lambda: None, ) v = util.ValueView(fake_minuit) f = util.FixedView(fake_minuit) f[1] = True assert_equal(f, [False, True, False]) assert_equal(v[f], [2]) assert_equal(v[~f], [1, 3]) v[f] = 5 assert_equal(v, [1, 5, 3]) v[~f] = [2, 4] assert_equal(v, [2, 5, 4]) def test_FixedView_comparison_with_broadcasting(): state = MnUserParameterState() state.add("x", 1, 0.1) state.add("y", 2, 0.1) state.add("z", 3, 0.1) fake_minuit = Namespace( _var2pos={"x": 0, "y": 1, "z": 2}, _pos2var=("x", "y", "z"), npar=3, _last_state=state, _copy_state_if_needed=lambda: None, ) f = util.FixedView(fake_minuit) assert_equal(f, [False, False, False]) # broadcasting assert f == False # noqa f[0] = True assert_equal(f, [True, False, False]) assert f != False # noqa def test_Matrix(): m = util.Matrix(("a", "b")) m[:] = [[1, 2], [2, 8]] assert_equal(m, ((1, 2), (2, 8))) assert repr(m) == "[[1. 2.]\n [2. 8.]]" c = m.correlation() assert_allclose(c, ((1.0, 0.5**0.5), (0.5**0.5, 1.0))) assert m["a", "b"] == 2.0 assert m["a", 1] == 2.0 assert m[1, "a"] == 2.0 assert m[1, 1] == 8.0 assert_equal(m[0], [1, 2]) assert_equal(m["b"], (2, 8)) assert_equal(m[:], [[1, 2], [2, 8]]) assert_equal(m[:, 0], [1, 2]) assert_equal(m[:, 1], [2, 8]) # this swaps rows and cols assert_equal(m[[1, 0]], [[8, 2], [2, 1]]) m *= 2 assert_equal(m, ((2, 4), (4, 16))) m2 = np.dot(m, (1, 1)) assert repr(m2) == "[ 6. 20.]" assert str(m2) == "[ 6. 20.]" assert_allclose(m2, (6, 20)) # matrix is always square m = util.Matrix(("a", "b", "c")) m[:] = np.arange(9).reshape((3, 3)) # [0 1 2 # 3 4 5 # 6 7 8] # m1 = m[:2] # assert_equal(m1, [[0, 1], [3, 4]]) m2 = m[[0, 2]] assert_equal(m2, [[0, 2], [6, 8]]) m3 = m[["a", "c"]] assert_equal(m3, [[0, 2], [6, 8]]) d = m.to_dict() assert list(d.keys()) == [ ("a", "a"), ("a", "b"), ("a", "c"), ("b", "b"), ("b", "c"), ("c", "c"), ] for k, v in d.items(): assert v == m[k] with pytest.raises(TypeError): util.Matrix("ab") with pytest.raises(TypeError): util.Matrix(1) m2 = pickle.loads(pickle.dumps(m)) assert type(m2) is util.Matrix assert_equal(m2, m) assert m2._var2pos == m._var2pos def test_Param(): values = 3, "foo", 1.2, 3.4, None, False, False, 42, None p = util.Param(*values) assert p.number == 3 assert p.name == "foo" assert p.value == 1.2 assert p.error == 3.4 assert p.merror is None assert not p.is_const assert not p.is_fixed assert p.has_limits assert p.has_lower_limit assert not p.has_upper_limit assert p.lower_limit == 42 assert p.upper_limit is None assert repr(p) == ( "Param(number=3, name='foo', value=1.2, error=3.4, merror=None, " "is_const=False, is_fixed=False, lower_limit=42, upper_limit=None)" ) def test_Params(): p = util.Params( [ util.Param(0, "foo", 1.2, 3.4, None, False, False, 42, None), util.Param(1, "bar", 3.4, 4.5, None, False, False, 42, None), ] ) assert repr(p) == repr((p[0], p[1])) assert p[0].number == 0 assert p[1].number == 1 assert p["foo"].number == 0 assert p["bar"].number == 1 def test_MError(): me = util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.7, ) assert repr(me) == ( "" ) assert me == util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.7, ) assert me != util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.8, ) def test_MErrors(): mes = util.MErrors( x=util.MError( 1, "x", 0.1, 0.2, True, True, True, False, False, False, False, False, False, 11, 0.7, ) ) assert repr(mes) == f"" @pytest.mark.parametrize("errordef", (0.5, 1.0)) def test_FMin(errordef): fm = Namespace( fval=1.23456e-10, edm=1.23456e-10, errordef=errordef, is_valid=True, has_valid_parameters=True, has_accurate_covar=True, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False, has_parameters_at_limit=False, state=[], ) fmin = util.FMin(fm, "foo", 1, 2, 1, 0.1, 1.2) assert {x for x in dir(fmin) if not x.startswith("_")} == { "algorithm", "edm", "edm_goal", "errordef", "fval", "reduced_chi2", "nfcn", "ngrad", "is_valid", "has_accurate_covar", "has_valid_parameters", "has_posdef_covar", "has_made_posdef_covar", "hesse_failed", "has_covariance", "is_above_max_edm", "has_reached_call_limit", "has_parameters_at_limit", "time", } assert fmin.algorithm == "foo" assert fmin.edm == 1.23456e-10 assert fmin.edm_goal == 0.1 assert not fmin.has_parameters_at_limit assert fmin.time == 1.2 assert fmin == util.FMin(fm, "foo", 1, 2, 1, 0.1, 1.2) assert fmin != util.FMin(fm, "foo", 1, 2, 1, 0.3, 1.2) assert fmin != util.FMin(fm, "bar", 1, 2, 1, 0.1, 1.2) assert fmin != util.FMin(fm, "foo", 1, 2, 1, 0.1, 1.5) if errordef == 1: reduced_chi2 = fmin.fval else: reduced_chi2 = np.nan assert repr(fmin) == ( f"" ) def test_normalize_limit(): assert util._normalize_limit(None) == (-np.inf, np.inf) assert util._normalize_limit((None, 2)) == (-np.inf, 2) assert util._normalize_limit((2, None)) == (2, np.inf) assert util._normalize_limit((None, None)) == (-np.inf, np.inf) with pytest.raises(ValueError): util._normalize_limit((3, 2)) def test_guess_initial_step(): assert util._guess_initial_step(0) == 0.1 assert util._guess_initial_step(1) == 0.01 def test_address_of_cfunc(): nb = pytest.importorskip("numba") nb_sig = nb.types.double(nb.types.uintc, nb.types.CPointer(nb.types.double)) @nb.cfunc(nb_sig) def fcn(n, x): x = nb.carray(x, (n,)) r = 0.0 for i in range(n): r += (x[i] - i) ** 2 return r from ctypes import cast, c_void_p, CFUNCTYPE, POINTER, c_double, c_uint32 address = cast(fcn.ctypes, c_void_p).value assert util._address_of_cfunc(fcn) == address # let's see if we can call the function pointer, going full circle c_sig = CFUNCTYPE(c_double, c_uint32, POINTER(c_double)) c_fcn = cast(address, c_sig) v = np.array((1.0, 2.0)) assert c_fcn(2, v.ctypes.data_as(POINTER(c_double))) == 2.0 def test_address_of_cfunc_bad_signature(): nb = pytest.importorskip("numba") nb_sig = nb.types.double(nb.types.double, nb.types.CPointer(nb.types.double)) @nb.cfunc(nb_sig) def fcn(y, x): return 0 assert util._address_of_cfunc(fcn) == 0 def test_make_func_code(): with pytest.warns(np.VisibleDeprecationWarning): fc = util.make_func_code(["a", "b"]) assert fc.co_varnames == ("a", "b") assert fc.co_argcount == 2 with pytest.warns(np.VisibleDeprecationWarning): fc = util.make_func_code(("x",)) assert fc.co_varnames == ("x",) assert fc.co_argcount == 1 def test_make_with_signature(): def f(a, b): return a + b f1 = util.make_with_signature(f, "x", "y") assert util.describe(f1) == ["x", "y"] assert f1(1, 2) == f(1, 2) f2 = util.make_with_signature(f, b="z") assert util.describe(f2) == ["a", "z"] assert f2(1, 2) == f(1, 2) assert f1 is not f2 f3 = util.make_with_signature(f, "x", b="z") assert util.describe(f3) == ["x", "z"] assert f3(1, 2) == f(1, 2) # check that arguments are not overridden assert util.describe(f1) == ["x", "y"] assert util.describe(f) == ["a", "b"] with pytest.raises(ValueError): util.make_with_signature(f, "a", "b", "c") with pytest.raises(ValueError): util.make_with_signature(f, "a", "b", "c", b="z") def test_make_with_signature_on_func_without_code_object(): class Fcn: def __call__(self, x, y): return x + y f = Fcn() assert not hasattr(f, "__code__") f1 = util.make_with_signature(f, "x", "y") assert util.describe(f1) == ["x", "y"] assert f1(1, 2) == f(1, 2) assert f1 is not f f2 = util.make_with_signature(f1, x="a") assert util.describe(f2) == ["a", "y"] assert f2(1, 2) == f(1, 2) def test_merge_signatures(): def f(x, y, z): return x + y + z def g(x, a, b): return x + a + b args, (pf, pg) = util.merge_signatures([f, g]) assert args == ["x", "y", "z", "a", "b"] assert pf == (0, 1, 2) assert pg == (0, 3, 4) @pytest.mark.skipif(not scipy_available, reason="needs scipy") def test_propagate_1(): cov = [ [1.0, 0.1, 0.2], [0.1, 2.0, 0.3], [0.2, 0.3, 3.0], ] x = [1, 2, 3] def fn(x): return 2 * x + 1 with pytest.warns(np.VisibleDeprecationWarning): y, ycov = util.propagate(fn, x, cov) np.testing.assert_allclose(y, [3, 5, 7]) np.testing.assert_allclose( ycov, [[4, 0.4, 0.8], [0.4, 8, 1.2], [0.8, 1.2, 12]], rtol=1e-3 ) with pytest.warns(np.VisibleDeprecationWarning): y, ycov = util.propagate(fn, [1], [[2]]) np.testing.assert_allclose(y, 3) np.testing.assert_allclose(ycov, 8, rtol=1e-3) @pytest.mark.skipif(not scipy_available, reason="needs scipy") def test_propagate_2(): cov = [ [1.0, 0.1, 0.2], [0.1, 2.0, 0.3], [0.2, 0.3, 3.0], ] x = [1.0, 2.0, 3.0] a = 0.5 * np.arange(30).reshape((10, 3)) def fn(x): return np.dot(a, x) with pytest.warns(np.VisibleDeprecationWarning): y, ycov = util.propagate(fn, x, cov) np.testing.assert_equal(y, fn(x)) np.testing.assert_allclose(ycov, np.einsum("ij,kl,jl", a, a, cov), rtol=1e-3) def fn(x): return np.linalg.multi_dot([x.T, cov, x]) with pytest.warns(np.VisibleDeprecationWarning): y, ycov = util.propagate(fn, x, cov) np.testing.assert_equal(y, fn(np.array(x))) jac = 2 * np.dot(cov, x) np.testing.assert_allclose(ycov, np.einsum("i,k,ik", jac, jac, cov), rtol=1e-3) @pytest.mark.skipif(not scipy_available, reason="needs scipy") def test_propagate_3(): # matrices with full zero rows and columns are supported cov = [ [1.0, 0.0, 0.2], [0.0, 0.0, 0.0], [0.2, 0.0, 3.0], ] x = [1.0, 2.0, 3.0] def fn(x): return 2 * x + 1 with pytest.warns(np.VisibleDeprecationWarning): y, ycov = util.propagate(fn, x, cov) np.testing.assert_allclose(y, [3, 5, 7]) np.testing.assert_allclose(ycov, [[4, 0.0, 0.8], [0.0, 0.0, 0.0], [0.8, 0.0, 12]]) @pytest.mark.skipif(not scipy_available, reason="needs scipy") def test_propagate_on_bad_input(): cov = [[np.nan, 0.0], [0.0, 1.0]] x = [1.0, 2.0] def fn(x): return 2 * x + 1 with pytest.warns(np.VisibleDeprecationWarning): with pytest.raises(ValueError): util.propagate(fn, x, cov) with pytest.warns(np.VisibleDeprecationWarning): with pytest.raises(ValueError): util.propagate(fn, x, 1) cov = [[1.0], [1.0]] with pytest.warns(np.VisibleDeprecationWarning): with pytest.raises(ValueError): util.propagate(fn, x, cov) def test_iterate(): assert list(util._iterate(1)) == [1] assert list(util._iterate([1, 2])) == [1, 2] def test_replace_none(): assert util._replace_none(None, 2) == 2 assert util._replace_none(3, 2) == 3 def test_progressbar(capsys): with util.ProgressBar(max_value=4) as bar: for i in range(4): bar += 1 stdout, stderr = capsys.readouterr() assert stdout == "\r0 %\r25 %\r50 %\r75 %\r100 %\r " def test_progressbar_no_ipykernel(capsys): with hide_modules("ipykernel"): with util.ProgressBar(max_value=4) as bar: for i in range(4): bar += 1 stdout, stderr = capsys.readouterr() assert stdout == "\r0 %\r25 %\r50 %\r75 %\r100 %\r " def test_progressbar_html(capsys): import sys m_iostream = pytest.importorskip("ipykernel.iostream") m_display = pytest.importorskip("IPython.display") class OutStream: def write(self, s): original_stdout.write(s) def flush(self): original_stdout.flush() def display(msg, **kwargs): sys.stdout.write(msg._repr_html_()) original_stdout = sys.stdout # make ProgressBar think it is running in Jupyter sys.stdout = OutStream() # monkey-patching our mockups m_iostream.OutStream = OutStream m_display.display = display with util.ProgressBar(max_value=4) as bar: for i in range(4): bar += 1 sys.stdout = original_stdout stdout, stderr = capsys.readouterr() assert stdout == ( " 0 %" " 25 %" " 50 %" " 75 %" " 100 %" ) @pytest.mark.parametrize( "mask_expected", ( (None, [[0, 1, 2]]), ([True, False], [[0]]), ([False, True], [[1]]), ([True, True, True, False, True, True, False, True], [[0, 1, 2], [4, 5], [7]]), ), ) def test_histogram_segments(mask_expected): mask, expected = mask_expected if mask is None: xe = np.linspace(0, 1, 4) masked = np.arange(3) else: xe = np.linspace(0, 1, len(mask)) masked = np.arange(len(mask))[np.array(mask)] segments = util._histogram_segments(mask, xe, masked) assert_equal([s[0] for s in segments], expected) @pytest.mark.parametrize( "fn_expected", ((lambda x: x, 15), (lambda x: x**11, 40), (np.log, 80)) ) def test_smart_sampling_1(fn_expected): fn, expected = fn_expected x, y = util._smart_sampling(fn, 1e-10, 5) assert len(y) < expected def test_smart_sampling_2(): # should not raise a warning x, y = util._smart_sampling(np.log, 1e-10, 1, tol=1e-5) assert 0 < len(x) < 1000 def test_smart_sampling_3(): def step(x): return np.where(x > 0.5, 0, 1) with pytest.warns(RuntimeWarning, match="Iteration limit"): x, y = util._smart_sampling(step, 0, 1, tol=0) assert 0 < len(x) < 80 def test_smart_sampling_4(): from time import sleep def step(x): sleep(0.1) return np.where(x > 0.5, 0, 1) with pytest.warns(RuntimeWarning, match="Time limit"): x, y = util._smart_sampling(step, 0, 1, maxtime=0) assert 0 < len(x) < 10 @pytest.mark.parametrize( "x,expected", [([1, 2, 3], False), ([-1, 1000, 100000], False), ([1, 10, 100], True)], ) def test_detect_log_spacing_1(x, expected): assert util._detect_log_spacing(x) == expected def test_optional_module_for_1(): with optional_module_for("foo"): import iminuit # noqa def test_optional_module_for_2(): from iminuit.warnings import OptionalDependencyWarning with pytest.warns( OptionalDependencyWarning, match="foo requires optional package 'foobarbaz'" ): with optional_module_for("foo"): import foobarbaz # noqa def test_optional_module_for_3(): from iminuit.warnings import OptionalDependencyWarning with pytest.warns( OptionalDependencyWarning, match="foo requires optional package 'foo'" ): with optional_module_for("foo", replace={"foobarbaz": "foo"}): import foobarbaz # noqa iminuit-2.24.0/tests/test_without_ipywidgets.py0000644000000000000000000000073114332717401016734 0ustar00from iminuit._hide_modules import hide_modules from iminuit.cost import LeastSquares import pytest pytest.importorskip("ipywidgets") def test_interactive(): import iminuit cost = LeastSquares([1.1, 2.2], [3.3, 4.4], 1, lambda x, a: a * x) iminuit.Minuit(cost, 1).interactive() with hide_modules("ipywidgets", reload="iminuit"): with pytest.raises(ModuleNotFoundError, match="Please install"): iminuit.Minuit(cost, 1).interactive() iminuit-2.24.0/tests/test_without_matplotlib.py0000644000000000000000000000071014332717401016710 0ustar00from iminuit import cost from iminuit._hide_modules import hide_modules import pytest pytest.importorskip("matplotlib.pyplot") def test_visualize(): import iminuit c = cost.LeastSquares([1, 2], [3, 4], 1, lambda x, a: a * x) s = iminuit.Minuit(c, 1).migrad()._repr_html_() assert " License: Minuit is from SEAL Minuit It's LGPL v2 http://seal.web.cern.ch/seal/main/license.html. For iminuit, I'm releasing it as MIT license: Copyright (c) 2012 Piti Ongmongkolkul 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. Note: MIT license is GPL compatible, so it is an acceptable license for a wrapper, as can be seen here: http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.html#GPLWrapper http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.html#OrigBSD (L)GPL can be combined or included in code that does not impose more restrictive conditions. Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Programming Language :: C++ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development Classifier: Topic :: Scientific/Engineering :: Physics Classifier: Topic :: Scientific/Engineering :: Mathematics Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Operating System :: MacOS Project-URL: Repository, http://github.com/scikit-hep/iminuit Project-URL: Documentation, https://iminuit.readthedocs.io Requires-Python: >=3.8 Requires-Dist: numpy>=1.21 Requires-Dist: typing_extensions; python_version < "3.9" Requires-Dist: coverage; extra == "test" Requires-Dist: cython; extra == "test" Requires-Dist: ipywidgets<8.0.5; extra == "test" Requires-Dist: ipykernel; extra == "test" Requires-Dist: joblib; extra == "test" Requires-Dist: jacobi; extra == "test" Requires-Dist: matplotlib; extra == "test" Requires-Dist: numpy; extra == "test" Requires-Dist: numba; extra == "test" Requires-Dist: numba-stats; extra == "test" Requires-Dist: pytest; extra == "test" Requires-Dist: scipy; extra == "test" Requires-Dist: tabulate; extra == "test" Requires-Dist: boost_histogram; extra == "test" Requires-Dist: resample; extra == "test" Requires-Dist: unicodeitplus; extra == "test" Requires-Dist: pydantic; extra == "test" Requires-Dist: annotated_types; extra == "test" Requires-Dist: sphinx<7; extra == "doc" Requires-Dist: sphinx-rtd-theme; extra == "doc" Requires-Dist: nbsphinx; extra == "doc" Requires-Dist: nbconvert; extra == "doc" Requires-Dist: nbformat; extra == "doc" Requires-Dist: jupyter_client; extra == "doc" Requires-Dist: ipykernel; extra == "doc" Requires-Dist: jax; extra == "doc" Requires-Dist: jaxlib; extra == "doc" Provides-Extra: test Provides-Extra: doc Description-Content-Type: text/x-rst .. |iminuit| image:: doc/_static/iminuit_logo.svg :alt: iminuit |iminuit| ========= .. version-marker-do-not-remove .. image:: https://scikit-hep.org/assets/images/Scikit--HEP-Project-blue.svg :target: https://scikit-hep.org .. image:: https://img.shields.io/pypi/v/iminuit.svg :target: https://pypi.org/project/iminuit .. image:: https://img.shields.io/conda/vn/conda-forge/iminuit.svg :target: https://github.com/conda-forge/iminuit-feedstock .. image:: https://coveralls.io/repos/github/scikit-hep/iminuit/badge.svg?branch=develop :target: https://coveralls.io/github/scikit-hep/iminuit?branch=develop .. image:: https://readthedocs.org/projects/iminuit/badge/?version=latest :target: https://iminuit.readthedocs.io/en/stable .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3949207.svg :target: https://doi.org/10.5281/zenodo.3949207 .. image:: https://img.shields.io/badge/ascl-2108.024-blue.svg?colorB=262255 :target: https://ascl.net/2108.024 :alt: ascl:2108.024 .. image:: https://img.shields.io/gitter/room/Scikit-HEP/iminuit :target: https://gitter.im/Scikit-HEP/iminuit .. image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/scikit-hep/iminuit/develop?filepath=doc%2Ftutorial *iminuit* is a Jupyter-friendly Python interface for the *Minuit2* C++ library maintained by CERN's ROOT team. Minuit was designed to minimize statistical cost functions, for likelihood and least-squares fits of parametric models to data. It provides the best-fit parameters and error estimates from likelihood profile analysis. The iminuit package comes with additional features: - Builtin cost functions for statistical fits - Binned and unbinned maximum-likelihood - `Template fits with error propagation `_ - Least-squares (optionally robust to outliers) - Gaussian penalty terms for parameters - Cost functions can be combined by adding them: ``total_cost = cost_1 + cost_2`` - Visualization of the fit in Jupyter notebooks - Support for SciPy minimizers as alternatives to Minuit's MIGRAD algorithm (optional) - Support for Numba accelerated functions (optional) Dependencies ------------ *iminuit* is (and always will be) a lean package which only depends on ``numpy``, but additional features are enabled if the following optional packages are installed. - ``matplotlib``: Visualization of fitted model for builtin cost functions - ``ipywidgets``: Interactive fitting, see example below (also requires ``matplotlib``) - ``scipy``: Compute Minos intervals for arbitrary confidence levels - ``unicodeitplus``: Render names of model parameters in simple LaTeX as Unicode Documentation ------------- Checkout our large and comprehensive list of `tutorials`_ that take you all the way from beginner to power user. For help and how-to questions, please use the `discussions`_ on GitHub or `gitter`_. **Lecture by Glen Cowan** `In the exercises to his lecture for the KMISchool 2022 `_, Glen Cowan shows how to solve statistical problems in Python with iminuit. You can find the lectures and exercises on the Github page, which covers both frequentist and Bayesian methods. `Glen Cowan `_ is a known for his papers and international lectures on statistics in particle physics, as a member of the Particle Data Group, and as author of the popular book `Statistical Data Analysis `_. In a nutshell ------------- ``iminuit`` can be used with a user-provided cost functions in form of a negative log-likelihood function or least-squares function. Standard functions are included in ``iminuit.cost``, so you don't have to write them yourself. The following example shows how to perform an unbinned maximum likelihood fit. .. code:: python import numpy as np from iminuit import Minuit from iminuit.cost import UnbinnedNLL from scipy.stats import norm x = norm.rvs(size=1000, random_state=1) def pdf(x, mu, sigma): return norm.pdf(x, mu, sigma) # Negative unbinned log-likelihood, you can write your own cost = UnbinnedNLL(x, pdf) m = Minuit(cost, mu=0, sigma=1) m.limits["sigma"] = (0, np.inf) m.migrad() # find minimum m.hesse() # compute uncertainties .. image:: doc/_static/demo_output.png :alt: Output of the demo in a Jupyter notebook Interactive fitting ------------------- ``iminuit`` optionally supports an interactive fitting mode in Jupyter notebooks. .. image:: doc/_static/interactive_demo.gif :alt: Animated demo of an interactive fit in a Jupyter notebook Faster than RooFit ------------------ When ``iminuit`` is used with cost functions and pdfs that are JIT-compiled with `numba`_ (JIT-compiled pdfs are provided by `numba_stats`_ ), the fit is up to 10x faster compared to an equivalent fit in the `RooFit`_ framework. The gain is particularly large when `numba`_ with auto-parallelization is compared to parallel computation in `RooFit`_. .. image:: doc/_static/roofit_vs_iminuit+numba.svg More information about this benchmark is given `in the Benchmark section of the documentation `_. Partner projects ---------------- * `numba_stats`_ provides faster implementations of probability density functions than scipy, and a few specific ones used in particle physics that are not in scipy. * `boost-histogram`_ from Scikit-HEP provides fast generalized histograms that you can use with the builtin cost functions. * `jacobi`_ provides a robust, fast, and accurate calculation of the Jacobi matrix of any transformation function and building a function for generic error propagation. Versions -------- **The current 2.x series has introduced breaking interfaces changes with respect to the 1.x series.** All interface changes are documented in the `changelog`_ with recommendations how to upgrade. To keep existing scripts running, pin your major iminuit version to <2, i.e. ``pip install 'iminuit<2'`` installs the 1.x series. .. _changelog: https://iminuit.readthedocs.io/en/stable/changelog.html .. _tutorials: https://iminuit.readthedocs.io/en/stable/tutorials.html .. _discussions: https://github.com/scikit-hep/iminuit/discussions .. _gitter: https://gitter.im/Scikit-HEP/iminuit .. _jacobi: https://github.com/hdembinski/jacobi .. _numba_stats: https://github.com/HDembinski/numba-stats .. _boost-histogram: https://github.com/scikit-hep/boost-histogram .. _numba: https://numba.pydata.org .. _RooFit: https://root.cern.ch/doc/master/namespaceRooFit.html