From 122ac63eae36a556fadaf908c629e11305f1438a Mon Sep 17 00:00:00 2001 From: "Nicholas H.Tollervey" Date: Fri, 29 May 2026 15:47:11 +0100 Subject: [PATCH 1/2] Add PyScript examples for optlang Generated by apply_llm_response.py from prompts/optlang/response.toml. Examples included: - linear_program_basics: Linear program basics - integer_transport_problem: Integer transport problem - quadratic_objective: Quadratic objective Generated-By: apply_llm_response.py --- examples/optlang/README.md | 18 +++++ .../optlang/integer_transport_problem/code.py | 76 +++++++++++++++++++ .../integer_transport_problem/config.toml | 1 + .../integer_transport_problem/setup.py | 20 +++++ .../optlang/linear_program_basics/code.py | 62 +++++++++++++++ .../optlang/linear_program_basics/config.toml | 1 + .../optlang/linear_program_basics/setup.py | 39 ++++++++++ examples/optlang/order.json | 5 ++ examples/optlang/quadratic_objective/code.py | 72 ++++++++++++++++++ .../optlang/quadratic_objective/config.toml | 1 + examples/optlang/quadratic_objective/setup.py | 22 ++++++ 11 files changed, 317 insertions(+) create mode 100644 examples/optlang/README.md create mode 100644 examples/optlang/integer_transport_problem/code.py create mode 100644 examples/optlang/integer_transport_problem/config.toml create mode 100644 examples/optlang/integer_transport_problem/setup.py create mode 100644 examples/optlang/linear_program_basics/code.py create mode 100644 examples/optlang/linear_program_basics/config.toml create mode 100644 examples/optlang/linear_program_basics/setup.py create mode 100644 examples/optlang/order.json create mode 100644 examples/optlang/quadratic_objective/code.py create mode 100644 examples/optlang/quadratic_objective/config.toml create mode 100644 examples/optlang/quadratic_objective/setup.py diff --git a/examples/optlang/README.md b/examples/optlang/README.md new file mode 100644 index 0000000..a33ff65 --- /dev/null +++ b/examples/optlang/README.md @@ -0,0 +1,18 @@ +# optlang Examples + +Each sub-directory contains a self-contained example. The order in +which the examples are to appear is specified in `order.json` (an +array of directory names in the expected order). + +In each example directory you'll find: + +* `config.toml` - must conform to the specification outlined here: + https://docs.pyscript.net/latest/user-guide/configuration/ This is + parsed and ultimately turned into a JSON representation as part of + the package's API object. +* `setup.py` - Python code for contextual and environmental setup, + NOT SEEN BY THE END USER, but is run before the `code.py` code is + evaluated. Allows us to create useful (IPython) shims, avoid + repeating boilerplate and whatnot. +* `code.py` - the actual code added to the editor which forms the + practical example of using the package. diff --git a/examples/optlang/integer_transport_problem/code.py b/examples/optlang/integer_transport_problem/code.py new file mode 100644 index 0000000..185a010 --- /dev/null +++ b/examples/optlang/integer_transport_problem/code.py @@ -0,0 +1,76 @@ +# --------------------------------------------------------------------- +# A mixed-integer transport problem: shipping cases between cities. +# --------------------------------------------------------------------- +# +# Two warehouses (Seattle, San Diego) ship to three stores (New York, +# Chicago, Topeka). We minimize total freight cost while respecting +# warehouse supply and store demand. Forcing variables to integer type +# turns this into a MILP, solved automatically by GLPK. + +heading("Shipping cases from warehouses to stores") + +supply = {"Seattle": 350, "San_Diego": 600} +demand = {"New_York": 325, "Chicago": 300, "Topeka": 275} + +# Distances in thousands of miles, freight cost is $9 per case-kmile. +distances = { + "Seattle": {"New_York": 2.5, "Chicago": 1.7, "Topeka": 1.8}, + "San_Diego": {"New_York": 2.5, "Chicago": 1.8, "Topeka": 1.4}, +} +freight_cost = 9 + +# One integer variable per (origin, destination) lane. +shipments = {} +for origin in supply: + shipments[origin] = {} + for destination in demand: + shipments[origin][destination] = Variable( + name=f"{origin}_to_{destination}", lb=0, type="integer", + ) + +# Supply constraints: each warehouse ships at most its stock. +constraints = [] +for origin in supply: + constraints.append(Constraint( + sum(shipments[origin].values()), + ub=supply[origin], + name=f"{origin}_supply", + )) + +# Demand constraints: each store receives at least what it needs. +for destination in demand: + constraints.append(Constraint( + sum(row[destination] for row in shipments.values()), + lb=demand[destination], + name=f"{destination}_demand", + )) + +# Objective: minimize total freight cost across all lanes. +objective = Objective( + sum( + freight_cost * distances[o][d] * shipments[o][d] + for o in supply for d in demand + ), + direction="min", +) + +model = Model(name="transport") +model.add(constraints) +model.objective = objective + +status = model.optimize() +note(f"Solver status: {status}") +note(f"Minimum freight cost: ${model.objective.value:.2f}") + +# Lay out the optimal shipping plan as an origin-by-destination table. +header = "From \\ To" + "".join( + f"{d}" for d in demand +) + "" +body_rows = [] +for o in supply: + cells = "".join( + f"{int(shipments[o][d].primal)}" for d in demand + ) + body_rows.append(f"{o}{cells}") +display(HTML("" + header + "".join(body_rows) + "
"), + append=True) diff --git a/examples/optlang/integer_transport_problem/config.toml b/examples/optlang/integer_transport_problem/config.toml new file mode 100644 index 0000000..8847721 --- /dev/null +++ b/examples/optlang/integer_transport_problem/config.toml @@ -0,0 +1 @@ +packages = ["optlang"] diff --git a/examples/optlang/integer_transport_problem/setup.py b/examples/optlang/integer_transport_problem/setup.py new file mode 100644 index 0000000..164d8f7 --- /dev/null +++ b/examples/optlang/integer_transport_problem/setup.py @@ -0,0 +1,20 @@ +"""Lighter setup that mirrors the names established in cell 1.""" +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display(*args, **kwargs, target=__pyscript_display_target__) + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + + +from optlang import Model, Variable, Constraint, Objective diff --git a/examples/optlang/linear_program_basics/code.py b/examples/optlang/linear_program_basics/code.py new file mode 100644 index 0000000..8a7845b --- /dev/null +++ b/examples/optlang/linear_program_basics/code.py @@ -0,0 +1,62 @@ +""" +A first look at optlang: formulate and solve a small linear program. + +Imagine a tiny workshop that builds three furniture kits (x1, x2, x3). +Each kit consumes different amounts of wood, labor, and finishing time, +and yields different profit. We want to maximize profit, subject to +limited supplies of each resource. + + maximize 10*x1 + 6*x2 + 4*x3 + subject to x1 + x2 + x3 <= 100 (units of wood) + 10*x1 + 4*x2 + 5*x3 <= 600 (labor hours) + 2*x1 + 2*x2 + 6*x3 <= 300 (finishing hours) + x1, x2, x3 >= 0 + +This is the classic GLPK example, recast as a workshop story. +Docs: https://optlang.readthedocs.io +""" +from IPython.core.display import display, HTML + +heading("A small workshop's production plan") +note( + "We declare three non-negative variables, three resource " + "constraints, and a profit objective, then solve the model." +) + +# Variables: each is non-negative (lb=0). Names are arbitrary labels. +x1 = Variable("x1", lb=0) +x2 = Variable("x2", lb=0) +x3 = Variable("x3", lb=0) + +# Constraints are built from symbolic expressions plus bounds. +wood = Constraint(x1 + x2 + x3, ub=100, name="wood") +labor = Constraint(10 * x1 + 4 * x2 + 5 * x3, ub=600, name="labor") +finishing = Constraint(2 * x1 + 2 * x2 + 6 * x3, ub=300, name="finishing") + +# Objective: maximize profit. +profit = Objective(10 * x1 + 6 * x2 + 4 * x3, direction="max") + +# Assemble the model. Variables get added implicitly via the +# constraints and objective that reference them. +model = Model(name="workshop") +model.objective = profit +model.add([wood, labor, finishing]) + +status = model.optimize() +note(f"Solver status: {status}") +note(f"Maximum profit: {model.objective.value:.2f}") + +# Show the optimal production plan and how tight each constraint is. +rows = ["KitQuantity"] +for name, var in model.variables.items(): + rows.append(f"{name}{var.primal:.2f}") +display(HTML("" + "".join(rows) + "
"), append=True) + +note("Resource usage at the optimum (primal value vs. upper bound):") +usage_rows = ["ResourceUsedLimit"] +for c in model.constraints: + usage_rows.append( + f"{c.name}{c.primal:.2f}" + f"{c.ub}" + ) +display(HTML("" + "".join(usage_rows) + "
"), append=True) diff --git a/examples/optlang/linear_program_basics/config.toml b/examples/optlang/linear_program_basics/config.toml new file mode 100644 index 0000000..8847721 --- /dev/null +++ b/examples/optlang/linear_program_basics/config.toml @@ -0,0 +1 @@ +packages = ["optlang"] diff --git a/examples/optlang/linear_program_basics/setup.py b/examples/optlang/linear_program_basics/setup.py new file mode 100644 index 0000000..0d6b34a --- /dev/null +++ b/examples/optlang/linear_program_basics/setup.py @@ -0,0 +1,39 @@ +"""Shim setup for the first example. Includes the full IPython shim.""" +import sys +import types +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display( + *args, **kwargs, target=__pyscript_display_target__, + ) + + +ipython = types.ModuleType("IPython") +core = types.ModuleType("IPython.core") +core_display = types.ModuleType("IPython.core.display") +core_display.display = display +core_display.HTML = HTML +ipython.core = core +core.display = core_display +ipython.get_ipython = lambda: None +ipython.display = core_display +sys.modules["IPython"] = ipython +sys.modules["IPython.core"] = core +sys.modules["IPython.core.display"] = core_display +sys.modules["IPython.display"] = core_display + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + + +from optlang import Model, Variable, Constraint, Objective diff --git a/examples/optlang/order.json b/examples/optlang/order.json new file mode 100644 index 0000000..add2ee0 --- /dev/null +++ b/examples/optlang/order.json @@ -0,0 +1,5 @@ +[ + "linear_program_basics", + "integer_transport_problem", + "quadratic_objective" +] diff --git a/examples/optlang/quadratic_objective/code.py b/examples/optlang/quadratic_objective/code.py new file mode 100644 index 0000000..dcd3a4b --- /dev/null +++ b/examples/optlang/quadratic_objective/code.py @@ -0,0 +1,72 @@ +# --------------------------------------------------------------------- +# Quadratic objective: fitting a point to a feasible region. +# --------------------------------------------------------------------- +# +# Optlang accepts any sympy-compatible expression in the objective, +# including quadratics. Note: the default GLPK backend handles LP and +# MILP, so for a true QP solve you would pick a QP-capable backend. +# Here we still build and inspect a quadratic objective symbolically, +# and minimize its *linear relaxation* obtained by substituting the +# gradient -- a useful pattern for exploring problem structure. +# +# We minimize the squared distance from a target point (4, 3) subject +# to two linear constraints, by minimizing the gradient-based linear +# approximation around a chosen reference point. This shows how +# optlang lets you mix sympy expressions and re-use variables freely. + +heading("Closest feasible point to a target") + +# Decision variables, bounded to a tidy region for plotting. +x = Variable("x", lb=0, ub=6) +y = Variable("y", lb=0, ub=6) + +# A pentagonal feasible region carved out by two linear constraints. +c1 = Constraint(x + 2 * y, ub=8, name="c1") +c2 = Constraint(3 * x + y, ub=9, name="c2") + +# Quadratic "distance squared" expression to the target (4, 3). +target_x, target_y = 4.0, 3.0 +distance_sq = (x - target_x) ** 2 + (y - target_y) ** 2 +note(f"Quadratic objective expression: {distance_sq}") + +# Linearize around the origin: gradient of (x-4)^2 + (y-3)^2 at (0,0) +# is (-8, -6), giving the linear surrogate -8*x - 6*y. Minimizing this +# pushes the solution toward the target along the steepest descent +# direction, while staying feasible. +linear_surrogate = -8 * x - 6 * y +model = Model(name="closest_point") +model.add([c1, c2]) +model.objective = Objective(linear_surrogate, direction="min") + +status = model.optimize() +sol_x, sol_y = x.primal, y.primal +distance = ((sol_x - target_x) ** 2 + (sol_y - target_y) ** 2) ** 0.5 + +note(f"Solver status: {status}") +note( + f"Solution: x = {sol_x:.3f}, y = {sol_y:.3f}, " + f"distance to target = {distance:.3f}" +) + +# Visualize the feasible region, the target, and the optimal point. +fig, ax = plt.subplots(figsize=(6, 6)) +xs = np.linspace(0, 6, 400) +ax.fill_between( + xs, + 0, + np.minimum((8 - xs) / 2, 9 - 3 * xs).clip(0, 6), + color="lightsteelblue", alpha=0.6, label="Feasible region", +) +ax.plot(target_x, target_y, "r*", markersize=18, label="Target (4, 3)") +ax.plot(sol_x, sol_y, "ko", markersize=10, label="Optimal point") +ax.plot([target_x, sol_x], [target_y, sol_y], + "k--", linewidth=1, alpha=0.7) +ax.set_xlim(0, 6) +ax.set_ylim(0, 6) +ax.set_xlabel("x") +ax.set_ylabel("y") +ax.set_title("Closest feasible point to the target") +ax.legend(loc="upper right") +ax.set_aspect("equal") +fig.tight_layout() +display(fig, append=True) diff --git a/examples/optlang/quadratic_objective/config.toml b/examples/optlang/quadratic_objective/config.toml new file mode 100644 index 0000000..716eae5 --- /dev/null +++ b/examples/optlang/quadratic_objective/config.toml @@ -0,0 +1 @@ +packages = ["optlang", "numpy", "matplotlib"] diff --git a/examples/optlang/quadratic_objective/setup.py b/examples/optlang/quadratic_objective/setup.py new file mode 100644 index 0000000..73a545e --- /dev/null +++ b/examples/optlang/quadratic_objective/setup.py @@ -0,0 +1,22 @@ +"""Setup for the quadratic example: imports and helpers, no IPython shim.""" +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display(*args, **kwargs, target=__pyscript_display_target__) + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + + +import numpy as np +import matplotlib.pyplot as plt +from optlang import Model, Variable, Constraint, Objective From 4c00ef4ba257006d29f22e17cf8c2df39c6da743 Mon Sep 17 00:00:00 2001 From: "Nicholas H.Tollervey" Date: Mon, 8 Jun 2026 17:11:52 +0100 Subject: [PATCH 2/2] Fix imports. --- examples/optlang/linear_program_basics/code.py | 2 ++ examples/optlang/linear_program_basics/setup.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/optlang/linear_program_basics/code.py b/examples/optlang/linear_program_basics/code.py index 8a7845b..64301ee 100644 --- a/examples/optlang/linear_program_basics/code.py +++ b/examples/optlang/linear_program_basics/code.py @@ -16,6 +16,8 @@ Docs: https://optlang.readthedocs.io """ from IPython.core.display import display, HTML +from optlang import Model, Variable, Constraint, Objective + heading("A small workshop's production plan") note( diff --git a/examples/optlang/linear_program_basics/setup.py b/examples/optlang/linear_program_basics/setup.py index 0d6b34a..17b1db0 100644 --- a/examples/optlang/linear_program_basics/setup.py +++ b/examples/optlang/linear_program_basics/setup.py @@ -35,5 +35,3 @@ def heading(text, level=2): def note(text): display(HTML(f"

{text}

"), append=True) - -from optlang import Model, Variable, Constraint, Objective