Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/juliac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,60 @@ jobs:
with:
name: jumpy-backend-${{ matrix.os }}
path: julia/build

docs:
name: Executable documentation
needs: build-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"

- name: Download compiled backend
uses: actions/download-artifact@v4
with:
name: jumpy-backend-ubuntu-latest
path: julia/build

- name: Build and execute documentation
run: uv run --frozen --group docs sphinx-build -M html docs/source docs/build -W --keep-going
env:
JUMPY_LIB: ${{ github.workspace }}/julia/build/lib/libjumpy_highs.so

- name: Upload documentation for inspection
uses: actions/upload-artifact@v4
with:
name: jumpy-documentation
path: docs/build/html

- name: Configure GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/configure-pages@v5

- name: Upload GitHub Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v4
with:
path: docs/build/html

deploy-docs:
name: Deploy documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: docs
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
concurrency:
group: github-pages
cancel-in-progress: false
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ dist/
build/
.venv/
build-trim/
docs/build/
docs/source/generated/
11 changes: 11 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

.PHONY: clean html

clean:
$(SPHINXBUILD) -M clean $(SOURCEDIR) $(BUILDDIR)

html:
$(SPHINXBUILD) -M html $(SOURCEDIR) $(BUILDDIR) -W --keep-going
50 changes: 50 additions & 0 deletions docs/source/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# API reference

## Models

```{eval-rst}
.. autoclass:: jumpy.Model
:members:

.. autofunction:: jumpy.minimize

.. autofunction:: jumpy.maximize
```

## Expressions

```{eval-rst}
.. autoclass:: jumpy.Variable
:members:

.. autoclass:: jumpy.VariableVector
:members:

.. autoclass:: jumpy.Parameter
:members:

.. autoclass:: jumpy.Node
:members:

.. autoclass:: jumpy.Constraint
:members:

.. autoclass:: jumpy.Objective
:members:
```

## Nonlinear functions

```{eval-rst}
.. autofunction:: jumpy.sin

.. autofunction:: jumpy.cos

.. autofunction:: jumpy.exp

.. autofunction:: jumpy.log

.. autofunction:: jumpy.sqrt

.. autofunction:: jumpy.jp_abs
```
47 changes: 47 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from __future__ import annotations

from pathlib import Path

project = "JuMPy"
author = "JuMPy contributors"
copyright = "2026, JuMPy contributors"
html_baseurl = "https://nexor-optimization.github.io/JuMPy/"

root_dir = Path(__file__).resolve().parents[2]

extensions = [
"myst_parser",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"sphinx_copybutton",
"sphinx_gallery.gen_gallery",
]

autosummary_generate = True
nitpicky = True

intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
}

html_theme = "sphinx_book_theme"
html_theme_options = {
"repository_url": "https://github.com/NexOR-Optimization/JuMPy",
"use_repository_button": True,
"use_issues_button": True,
}

sphinx_gallery_conf = {
"examples_dirs": str(root_dir / "docs" / "tutorials"),
"gallery_dirs": "generated/tutorials",
"filename_pattern": r".*\.py",
"abort_on_example_error": True,
"only_warn_on_example_error": False,
# JuMPy tutorials currently produce text output only. Disable Gallery's
# default Matplotlib scraper and module resetter so docs stay lightweight.
"image_scrapers": (),
"reset_modules": (),
"remove_config_comments": True,
"show_memory": False,
}
16 changes: 16 additions & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# JuMPy documentation

JuMPy is a Python interface to
[MathOptInterface](https://github.com/jump-dev/MathOptInterface.jl) via
[GenOpt](https://github.com/blegat/GenOpt.jl). It builds compact optimization
model templates in Python and expands them in a compiled Julia backend.

```{toctree}
:maxdepth: 2
:caption: Contents

installation
generated/tutorials/index
api
tutorial_licenses
```
23 changes: 23 additions & 0 deletions docs/source/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Installation

Install JuMPy from PyPI:

```console
pip install jumpy
```

The default `juliac` backend uses the compiled HiGHS backend shipped with
JuMPy and does not require a Julia installation.

## Development documentation

The executable tutorials require a locally built JuMPy library. Follow the
[compiled backend build instructions](https://github.com/NexOR-Optimization/JuMPy/blob/main/julia/README.md),
then build the documentation with:

```console
uv run --group docs sphinx-build -M html docs/source docs/build -W --keep-going
```

Set `JUMPY_LIB` to the compiled library if it is not in the default development
location.
37 changes: 37 additions & 0 deletions docs/source/sg_execution_times.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

:orphan:

.. _sphx_glr_sg_execution_times:


Computation times
=================
**00:00.000** total execution time for 1 file **from all galleries**:

.. container::

.. raw:: html

<style scoped>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css" rel="stylesheet" />
</style>
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" class="init">
$(document).ready( function () {
$('table.sg-datatable').DataTable({order: [[1, 'desc']]});
} );
</script>

.. list-table::
:header-rows: 1
:class: table table-striped sg-datatable

* - Example
- Time
- Mem (MB)
* - :ref:`sphx_glr_generated_tutorials_knapsack.py` (``../tutorials/knapsack.py``)
- 00:00.000
- 0.0
13 changes: 13 additions & 0 deletions docs/source/tutorial_licenses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Tutorial provenance and licenses

Some JuMPy tutorials are Python translations and adaptations of tutorials from
the [JuMP documentation](https://jump.dev/JuMP.jl/stable/tutorials/). We make
that provenance explicit on every translated page and link to the exact source
revision used for the translation.

Translated tutorial source files retain the copyright and license notices of
their originals. In particular, files adapted from JuMP tutorials under the
Mozilla Public License 2.0 remain available under the
[Mozilla Public License 2.0](https://mozilla.org/MPL/2.0/). These per-file
notices take precedence over the JuMPy repository's default license for those
files.
9 changes: 9 additions & 0 deletions docs/tutorials/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
JuMPy tutorials
===============

Every tutorial in this gallery is an ordinary Python program. The documentation
build executes each file and captures its output. You can also run a tutorial
directly with Python.

Assertions and other validation code enclosed by Sphinx-Gallery's ignore
markers execute normally but are omitted from the rendered tutorial.
77 changes: 77 additions & 0 deletions docs/tutorials/knapsack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
#
# This Source Code Form is subject to the terms of the Mozilla Public License
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
r"""
The knapsack problem
====================

This tutorial demonstrates how to formulate and solve a binary integer linear
program with JuMPy. Given a collection of items and a capacity, we choose the
items with the greatest combined profit whose combined weight fits within the
capacity.

This is a Python translation and adaptation of JuMP's
`knapsack tutorial <https://github.com/jump-dev/JuMP.jl/blob/b1888e6e9e6f07b03529340216fc07ba95f91fd5/docs/src/tutorials/linear/knapsack.jl>`_.
The original tutorial was adapted to the current JuMPy API and its compiled
HiGHS backend.

The model is

.. math::

\begin{aligned}
\max \quad & \sum_{i=1}^n c_i x_i \\
\text{s.t.} \quad & \sum_{i=1}^n w_i x_i \leq C, \\
& x_i \in \{0, 1\}, \quad i = 1, \ldots, n,
\end{aligned}

where :math:`C` is the capacity and item :math:`i` has profit :math:`c_i` and
weight :math:`w_i`.
"""

import jumpy as jp

# Data
# ----
#
# Our example has five items and a capacity of 10 units.

profit = [5.0, 3.0, 2.0, 7.0, 4.0]
weight = [2.0, 8.0, 4.0, 2.0, 5.0]
capacity = 10.0
n = len(weight)

# Formulation
# -----------
#
# A binary variable records whether each item is selected.

model = jp.Model()
x = model.variables(n, binary=True, name="x")

# JuMPy expressions use ordinary Python arithmetic. Here, ``sum`` constructs
# the capacity constraint and objective one term at a time.

model.constraint(sum(weight[i] * x[i] for i in range(n)) <= capacity)
model.objective = jp.maximize(sum(profit[i] * x[i] for i in range(n)))

# Solution
# --------

model.optimize()
items_chosen = [i for i in range(n) if model.value(x[i]) > 0.5]
total_profit = sum(profit[i] for i in items_chosen)

print(f"Chosen item indices: {items_chosen}")
print(f"Total profit: {total_profit}")

# The assertions below are executed when the script or documentation runs, but
# they are removed from the rendered tutorial.

# sphinx_gallery_start_ignore
assert items_chosen == [0, 3, 4]
assert total_profit == 16.0
model.close()
# sphinx_gallery_end_ignore
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ dependencies = []
juliacall = ["juliacall>=0.9.23"]

[dependency-groups]
docs = [
"myst-parser>=4.0",
"sphinx>=8.1,<9",
"sphinx-book-theme>=1.1",
"sphinx-copybutton>=0.5",
"sphinx-gallery>=0.19",
]
tests = ["pytest>=8.0"]

[tool.pytest.ini_options]
Expand Down
Loading
Loading