Skip to content

Commit

Permalink
improve draw tests, revert adding locator argument to draw_mncontour (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
HDembinski authored Aug 15, 2021
1 parent 204381b commit 9877511
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 37 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ MANIFEST
htmlcov
cover
coverage.xml

tests/fig/*.svg
5 changes: 0 additions & 5 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ Changelog
2.8.2 (August 15, 2021)
-----------------------

Improvements
~~~~~~~~~~~~
- ``Minuit.draw_mncontour`` now draws the numerical label on top of the curve by
default. The location can be configured with the ``location`` keyword.

Fixes
~~~~~
- ``Minuit.draw_mncontour`` can now be used by passing a single float to keyword ``cl``,
Expand Down
18 changes: 10 additions & 8 deletions src/iminuit/minuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ def draw_contour(
bound: Union[float, Tuple[Tuple[float, float], Tuple[float, float]]] = 2,
) -> Tuple[Collection[float], Collection[float], Collection[Collection[float]]]:
"""
Draw 2D contour around minimum (required matplotlib).
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.
Expand Down Expand Up @@ -1806,7 +1806,6 @@ def draw_mncontour(
*,
cl: Optional[Iterable[float]] = None,
size: int = 100,
locator="top",
) -> Any:
"""
Draw 2D Minos confidence region (requires matplotlib).
Expand All @@ -1827,8 +1826,6 @@ def draw_mncontour(
size :
Number of points on each contour(s) (default: 100). Increasing this makes
the contour smoother, but requires more computation time.
locator : str
Where to place the numerical label (default: "top").
Examples
--------
Expand All @@ -1845,20 +1842,25 @@ def draw_mncontour(
mncontour
"""
from matplotlib import pyplot as plt
from matplotlib.path import Path
from matplotlib.contour import ContourSet

cls = cl if isinstance(cl, Iterable) else [cl]

c_val = []
c_pts = []
codes = []
for cl in cls: # type:ignore
pts = self.mncontour(x, y, cl=cl, size=size)
# close curve
pts = list(pts)
pts.append(pts[0])
# add extra point whose coordinates are ignored
pts = np.append(pts, pts[:1], axis=0)
c_val.append(cl if cl is not None else 0.68)
c_pts.append([pts]) # level can have more than one contour in mpl
cs = ContourSet(plt.gca(), c_val, c_pts, locator="top")
codes.append(
[[Path.MOVETO] + [Path.LINETO] * (size - 2) + [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(x)
plt.ylabel(y)
Expand Down
79 changes: 55 additions & 24 deletions tests/test_draw.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,88 @@
import pytest
from iminuit import Minuit
from pathlib import Path
import numpy as np


mpl = pytest.importorskip("matplotlib")
plt = pytest.importorskip("matplotlib.pyplot")
mpl.use("Agg")


def f1(x, y):
return (1 - x) ** 2 + 100 * (y - 1) ** 2


def f2(par):
return f1(par[0], par[1])
return (1 - x) ** 2 + np.exp((y - 1) ** 2)


f1.errordef = 1
f2.errordef = 1


@pytest.fixture(params=("normal", "numpy"))
def minuit(request):
if request.param == "normal":
m = Minuit(f1, x=0, y=0)
else:
m = Minuit(f2, (0, 0), name=("x", "y"))
@pytest.fixture
def minuit():
m = Minuit(f1, x=0, y=0)
m.migrad()
return m


def test_profile(minuit):
minuit.draw_profile("x") # plots with hesse errors
@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"))
del fig


@pytest.mark.parametrize("arg", ("x", "y"))
def test_profile_1(fig, minuit, arg):
# plots with hesse errors
minuit.draw_profile(arg)
plt.ylim(0, 5)


@pytest.mark.parametrize("arg", ("x", "y"))
def test_profile_2(fig, minuit, arg):
# plots with minos errors
minuit.minos()
minuit.draw_profile("x") # plots with minos errors
minuit.draw_profile(arg)
plt.ylim(0, 5)


def test_mnprofile(minuit):
minuit.draw_mnprofile("x") # plots with hesse errors
@pytest.mark.parametrize("arg", ("x", "y"))
def test_mnprofile_1(fig, minuit, arg):
# plots with hesse errors
minuit.draw_mnprofile(arg)
plt.ylim(0, 5)


@pytest.mark.parametrize("arg", ("x", "y"))
def test_mnprofile_2(fig, minuit, arg):
# plots with minos errors
minuit.minos()
minuit.draw_mnprofile("x") # plots with minos errors
minuit.draw_mnprofile(arg)
plt.ylim(0, 5)


def test_mncontour_1(minuit):
def test_mncontour_1(fig, minuit):
minuit.draw_mncontour("x", "y")


def test_mncontour_2(minuit):
def test_mncontour_2(fig, minuit):
minuit.draw_mncontour("x", "y", cl=0.68)


def test_mncontour_3(minuit):
def test_mncontour_3(fig, minuit):
minuit.draw_mncontour("x", "y", cl=[0.68, 0.9])


def test_drawcontour(minuit):
def test_contour_1(fig, minuit):
minuit.draw_contour("x", "y")
minuit.draw_contour("x", "x", size=20, bound=2)
minuit.draw_contour("x", "x", size=20, bound=((-10, 10), (-10, 10)))


def test_contour_2(fig, minuit):
minuit.draw_contour("x", "y", size=20, bound=2)


def test_contour_3(fig, minuit):
minuit.draw_contour("x", "y", size=100, bound=((-0.5, 2.5), (-1, 3)))

0 comments on commit 9877511

Please sign in to comment.