Metadata-Version: 2.4
Name: utility-viz
Version: 2.0.0b1
Summary: A Python toolkit for producing publication-quality microeconomics diagrams.
Author: Pin Yue Sung
Author-email: Pin Yue Sung <contact@econ-viz.org>
License-Expression: MIT
License-File: LICENSE
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Dist: numpy>=1.24.0
Requires-Dist: matplotlib>=3.6.0,<4.0.0
Requires-Dist: scipy>=1.13.0,<2.0.0
Requires-Dist: sympy>=1.12.0,<2.0.0
Requires-Dist: tomli>=2.0.0 ; python_full_version < '3.11'
Requires-Dist: mosaickit>=0.5.1,<0.6.0
Requires-Dist: bezierkit>=0.5.0rc1,<0.6.0
Requires-Dist: pillow>=9.0.0 ; extra == 'all'
Requires-Dist: ipywidgets>=8.0.0 ; extra == 'all'
Requires-Dist: ipython>=8.0.0 ; extra == 'all'
Requires-Dist: pillow>=9.0.0 ; extra == 'animation'
Requires-Dist: ipywidgets>=8.0.0 ; extra == 'interactive'
Requires-Dist: ipython>=8.0.0 ; extra == 'interactive'
Requires-Python: >=3.10, <4.0
Project-URL: Homepage, https://econ-viz.org
Project-URL: Documentation, https://econ-viz.org
Project-URL: Repository, https://github.com/EconViz/utility-viz
Project-URL: Bug Tracker, https://github.com/EconViz/utility-viz/issues
Project-URL: Changelog, https://github.com/EconViz/utility-viz/blob/main/CHANGELOG.md
Provides-Extra: all
Provides-Extra: animation
Provides-Extra: interactive
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/EconViz/econ-viz-docs/main/docs/assets/banner.svg" alt="utility-viz" width="480">
</p>

<p align="center">
  <a href="https://pypi.org/project/utility-viz/"><img alt="PyPI" src="https://img.shields.io/pypi/v/utility-viz?style=flat-square&color=181818&labelColor=f3f3f3&cacheSeconds=300"></a>
  <a href="https://pypi.org/project/utility-viz/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/utility-viz?style=flat-square&color=181818&labelColor=f3f3f3"></a>
  <a href="https://opensource.org/licenses/MIT"><img alt="License" src="https://img.shields.io/badge/License-MIT-181818?style=flat-square&color=181818&labelColor=f3f3f3"></a>
  <img alt="Tests" src="https://img.shields.io/badge/tests-557%20passed-181818?style=flat-square&color=181818&labelColor=f3f3f3">
  <img alt="Coverage" src="https://img.shields.io/badge/coverage-92.63%25-181818?style=flat-square&color=181818&labelColor=f3f3f3">
</p>

A Python toolkit for producing publication-quality microeconomics diagrams. Define utility functions declaratively, solve for consumer equilibria, and export figures as PNG, PDF, SVG, or pure TikZ — all in a few lines of code.

## Installation

```bash
pip install utility-viz
```

Requires Python 3.10 or later.

## Quick Start

```python
from utility_viz import Canvas, levels, solve
from utility_viz.models import CobbDouglas

model = CobbDouglas(alpha=0.5, beta=0.5)
eq = solve(model, px=2.0, py=3.0, income=30.0)
lvls = levels.around(eq.utility, n=5)

cvs = Canvas(x_max=20, y_max=15, x_label="x", y_label="y", title="Cobb-Douglas  $x^{0.5} y^{0.5}$")
cvs.add_utility(model, levels=lvls)
cvs.add_budget(2.0, 3.0, 30.0, fill=True)
cvs.add_equilibrium(eq, show_ray=True)
cvs.save("cobb_douglas.png")
```

TikZ export writes a standalone LaTeX document with only TikZ drawing commands:

```python
cvs.save("cobb_douglas.tex", tikz_scale=0.0125)
```

The default TikZ scale maps a 6 inch wide Matplotlib figure to about 7.5 cm.

![Cobb-Douglas indifference map with budget line and equilibrium point](https://raw.githubusercontent.com/EconViz/utility-viz/a8423043789ee7dba19b2d71fa6cc5071601181a/cobb_douglas_eq.png)

## Notebook

The project ships with an interactive playground notebook:

[`notebook/econ-viz Playground.ipynb`](notebook/econ-viz%20Playground.ipynb)

Download it and open it in Jupyter, VS Code, or Colab. The first code cell upgrades `utility-viz` from PyPI for fresh runtimes.

## Highlights

- Built-in models: Cobb-Douglas, Leontief, Perfect Substitutes, CES, Satiation, Quasi-Linear, Stone-Geary, and Translog
- Solver support for interior, kink, boundary, and corner solutions
- Closed-form demand helpers with `solution_tex(...)`
- Comparative tools including `comparative_statics(...)` and `slutsky_matrix(...)`
- Multi-panel `Figure` layouts, `PricePath` / `IncomePath`, and linked `DemandDiagram`
- CLI support for plotting and closed-form demand output
- Color-blind-friendly default palette (`themes.COLORBLIND_CYCLE_RGB`) sourced from [thriveth/8560036](https://gist.github.com/thriveth/8560036), with related citation at [DOI:10.1080/00220485.1996.10844911](https://www.tandfonline.com/doi/abs/10.1080/00220485.1996.10844911)

## Additional Tools

Axis labels can be placed around their arrowheads, and each axis can use its
own arrowhead style and line style (solid, dashed, dotted, or dashdot):

```python
from utility_viz import ArrowStyle, Canvas, LabelPosition, LineStyle

canvas = Canvas(
    x_label_pos=LabelPosition.TOP,
    y_label_pos=LabelPosition.RIGHT,
    x_arrow_style=ArrowStyle.SIMPLE,
    y_arrow_style=ArrowStyle.WEDGE,
    x_line_style=LineStyle.DASHED,
)
```

Every line can be restyled with a `Stroke`: width, line style, colour, and an
arrowhead at its end. Fields you leave out keep the theme default (see the
`*_stroke` defaults on `Theme`, such as `theme.budget_stroke`):

```python
from utility_viz import ArrowStyle, Stroke

canvas = Canvas(axis_stroke=Stroke(width=1.4, arrow=ArrowStyle.SIMPLE))
canvas.add_budget(2, 3, 30, stroke=Stroke(width=3, style="dashed"))
canvas.add_equilibrium(eq, drop_stroke=Stroke(style="dashdot"))
canvas.add_ray(0.5, stroke=Stroke(arrow=ArrowStyle.TRIANGLE))
```

`add_utility`, `add_path`, `add_decomposition`, `DemandDiagram`, and
`EdgeworthBox` take one `*_stroke` argument per kind of line they draw.
`Stroke` is the preferred way to style lines; the separate `color`,
`linewidth`, and `linestyle` arguments still work as shorthand and draw the
same thing.

Point markers work the same way with `Marker` (colour, size, and shape);
fields you leave out keep the theme default, such as `theme.eq_marker`:

```python
from utility_viz import Marker

canvas.add_equilibrium(eq, marker=Marker(shape="s", size=8))
canvas.add_point(12, 2, label="A", marker=Marker(color="black", shape="D"))
canvas.add_decomposition(dec, point_marker=Marker(shape="^"))
```

Point labels take a `Label` (text, position, offset, colour, size, and
visibility) wherever a plain string worked. A label follows its point's
`Marker` colour unless it sets its own:

```python
from utility_viz import Label

canvas.add_equilibrium(eq, label=Label(position="bottom-left", offset=8))
canvas.add_point(12, 2, label=Label(text="A", position="left", fontsize=14))
canvas.add_utility(u, levels=3, ic_label=Label(text="U={:.1f}", position="top"))
canvas.add_decomposition(dec, point_label=Label(visible=False))  # hide A, B, C
```

The same `Label` styles every other piece of text: axis labels, the origin
`0`, titles, effect labels, and the Edgeworth box's good names and origins:

```python
canvas = Canvas(
    title=Label(text="Hicks decomposition", fontsize=13),
    x_axis=Axis(label=Label(text="x_1", fontsize=16)),
    origin_label=Label(visible=False),
)
canvas.add_decomposition(dec, substitution=Effect(label=Label(text="SE", fontsize=12)))
```

Shade the budget set with `fill=True`, or pass a `Fill` for a colour and
opacity of its own (default `theme.budget_fill`, coloured like the line):

```python
from utility_viz import Fill

canvas.add_budget(2, 3, 30, color="black", fill=Fill(color="lightgrey", opacity=0.4))
```

Every style object takes an `opacity` from 0 to 1, for example to show the
original budget line faintly:

```python
canvas.add_budget(2, 3, 30, stroke=Stroke(opacity=0.35))
canvas.add_decomposition(dec, income=Effect(opacity=0.5), legend=Legend(opacity=0.8))
```

Each axis's label, label position, and stroke fit in one `Axis`, accepted by
`Canvas`, `Figure`, `DemandDiagram`, and `EdgeworthBox`. `x_label`,
`x_label_pos`, and `x_axis_stroke` stay as shorthand; an `Axis` field wins
when both are set:

```python
from utility_viz import Axis, Stroke

canvas = Canvas(
    x_axis=Axis(label="x_1", label_position="bottom", stroke=Stroke(width=1.2)),
    y_axis=Axis(label="x_2"),
)
```

Legends go where they cover the least of the diagram by default, moving
outside the plot area when every corner is taken. Pass a `Legend` to choose
an inside corner (`"upper left"`, …) or a side outside (`"top"`, `"bottom"`,
`"left"`, `"right"`), or to change its font size, frame, and columns:

```python
from utility_viz import Legend

canvas.add_decomposition(dec, legend=Legend(position="bottom"))
canvas.show_legend(legend=Legend(position="upper left", fontsize=10))
```

Set a font for one canvas or a whole multi-panel figure without touching
Matplotlib's global settings. Pass a family name, a generic family such as
`"serif"`, or a fallback list:

```python
from utility_viz import Figure, Layout

canvas = Canvas(font=["Times New Roman", "serif"], math_font="stix")
figure = Figure(Layout.SIDE_BY_SIDE, font="serif", math_font="stix")
```

`font` applies to titles, axis labels, annotations, curve labels, and legends.
Math text, including the default axis labels, uses `math_font`: `"stix"`
(Times-like), `"cm"` (Computer Modern), `"dejavuserif"`, `"dejavusans"`, or
`"stixsans"`. An unavailable font raises
`InvalidParameterError`. TikZ output uses the LaTeX document's fonts, so only
generic families are mapped (`serif` → `\rmfamily`, `monospace` → `\ttfamily`).

Closed-form Marshallian demand in TeX:

```python
from utility_viz import solution_tex
from utility_viz.models import CobbDouglas

tex = solution_tex(CobbDouglas(alpha=0.4, beta=0.6))
```

Slutsky matrix:

```python
from utility_viz import slutsky_matrix
from utility_viz.models import CobbDouglas

S = slutsky_matrix(CobbDouglas(alpha=0.4, beta=0.6), px=2.0, py=3.0, income=60.0)
# S.s_xx, S.s_xy, S.s_yx, S.s_yy
```

## Settings file

Keep your style in an `utility-viz.toml` and load it once. Section names match
Theme properties (`[stroke.budget]` is `theme.budget_stroke`), fields match the
style objects, and anything left out keeps the default:

```toml
[color]
ic = "#2E86AB"

[stroke.budget]
width = 1.5

[label.point]
fontsize = 12

[legend]
position = "bottom"
```

```python
from utility_viz import Config

Config.load("utility-viz.toml").use()  # diagrams created from now on use it
```

`utility-viz init` writes a commented template, and `utility-viz plot --config
utility-viz.toml ...` uses the same file. Arguments passed to a method still win
over the file.

## CLI

```bash
utility-viz --version
utility-viz help
utility-viz models
utility-viz solve-tex --model cobb-douglas --symbolic-params
```

Plotting example:

```bash
utility-viz plot --model cobb-douglas --alpha 0.5 --beta 0.5 \
              --px 2 --py 3 --income 30 \
              --fill --show-ray \
              --output cobb_douglas.png
```

## Migrating from econ-viz

`econ-viz` was renamed to **utility-viz** in 2.0.0.

| | 1.x | 2.x |
|---|---|---|
| Distribution | `pip install econ-viz` | `pip install utility-viz` |
| Import | `import econ_viz` | `import utility_viz` |
| CLI | `econ-viz` | `utility-viz` |
| Config file | `econ-viz.toml` | `utility-viz.toml` (section names unchanged) |

**Which package to install.** `utility-viz` ships only `utility_viz` and the `utility-viz` command: it has no
`econ_viz` package and no `econ-viz` command. `econ-viz` 2.x (same version number) is a thin compatibility
distribution: `pip install econ-viz` installs `utility-viz` of the same version plus the `econ_viz` package and
the `econ-viz` command, which warn that they are deprecated. Upgrading an existing 1.x installation with
`pip install --upgrade econ-viz` therefore keeps working and moves you onto 2.x. Pre-releases need `--pre`
(for example `pip install --pre --upgrade econ-viz`). Switch to `pip install utility-viz` when you are ready
to drop the compatibility layer.

**Compatibility layer.** Throughout 2.x the `econ-viz` distribution provides an
`econ_viz` package and an `econ-viz` command so documented 1.x code keeps working:

- `import econ_viz` emits one deprecation warning per process.
- `from econ_viz import ...` and the documented sub-modules (`econ_viz.models`, `econ_viz.optimizer`,
  `econ_viz.themes`, ...) resolve to their `utility_viz` equivalents. Names that did not change are the
  very same objects.
- Constructing `econ_viz.Canvas`, `econ_viz.Figure` or `econ_viz.animation.Animator`, or accessing
  `econ_viz.Layout`, emits a `utility_viz.UtilityVizDeprecationWarning` (a `FutureWarning`) stating
  "deprecated since 2.0.0, removed in 3.0.0" and the replacement. `Figure`, `Layout` and `Animator` map to
  their current 2.x equivalents; their declarative replacements (`CanvasGrid`, `Animation`) are planned and
  named in the message as such.
- Config lookup: explicit path, then `utility-viz.toml`, then legacy `econ-viz.toml` (with a warning), then
  defaults. If both files exist the new one wins and the legacy one is ignored with a warning.
  `Config.load()` with no argument follows the file order and raises if neither file exists (as in 1.x);
  `Config.discover()` falls back to defaults; `Config.load("file.toml")` reads exactly that file.
  `utility-viz plot` applies the same lookup in the current directory when `--config` is not given.
- The `econ-viz` command prints a deprecation warning and forwards to `utility-viz`.
- `utility-viz init --migrate` writes `utility-viz.toml` from `econ-viz.toml` and keeps the old file.

**Removal boundary (3.0.0).** The `econ_viz` package, the `econ-viz` command and `econ-viz.toml` lookup are
removed in 3.0.0, not 2.0.0. Only the documented 1.x public API is covered; undocumented deep module paths
(for example `econ_viz.canvas.renderers.*`) resolve on a best-effort basis and may disappear at any time.
Internal `utility_viz.core.*` modules are advanced APIs and not part of the compatibility contract.

## Documentation

Full documentation lives at [econ-viz.org](https://econ-viz.org).

## License

MIT © Pin Yue Sung
