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
40 changes: 40 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Documentation

on:
push:
branches:
- main
tags:
- '*'
pull_request:

jobs:
build:
permissions:
actions: write
contents: write
pull-requests: read
statuses: write
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- uses: julia-actions/setup-julia@v3
with:
version: '1.12'

- uses: julia-actions/cache@v3

- name: Install documentation dependencies
shell: julia --color=yes --project=docs {0}
run: |
using Pkg
Pkg.develop(PackageSpec(path = "."))
Pkg.instantiate()

- name: Build and deploy documentation
run: julia --color=yes --project=docs docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@
*.log
tutorials/.DS_Store
.DS_Store
/Manifest*.toml
/docs/build/
/docs/Manifest*.toml
/docs/src/generated_examples/
/docs/src/examples.md
/docs/src/changelog.md
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Changelog

All notable changes to `PEPit.jl` are documented in this file. The format is
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.2] - 2026-06-08

### Added
- Documenter.jl documentation scaffold with API reference pages, an executable
Quick Start, tutorials, contributing guide, release notes, and custom styling.
- GitHub Actions workflow for building and deploying the documentation site.
- Comprehensive source docstrings for the public API and key implementation
helpers across the core objects, function classes, operator classes,
primitive steps, and utilities.
- Mathematical docstrings for all `wc_*` example functions.
- Executable, expanded Quick Start covering worst-case instance recovery with
`evaluate`, the explicit dual certificate (`solve_dual!`, `eval_dual`,
`DualPEPCertificate`), and dimension-reduction heuristics (`tracetrick`,
`logdetiters`).
- Contributing guide with templates for adding function/operator classes,
primitive steps, and worked examples.
- Automatically generated Examples overview, category pages, and per-example
pages extracted from the corresponding `wc_*` function docstrings.
- This changelog and a "Release notes" page in the documentation.

### Changed
- `evaluate` is now exported. It is the documented way to recover numerical
realizations of `Point`/`Expression` objects after solving.
- Standardized all documentation links and documentation deployment on
`github.com/PerformanceEstimation/PEPit.jl`.
- Tests now access internal counters and helper functions as `PEPit.<name>`
after those symbols were removed from the public export list.

### Fixed
- `evaluate` was documented and shown in the Quick Start but not exported, so
user code following the Quick Start raised `UndefVarError`.
- Broken display-math block in the `inexact_gradient_step!` docstring: the
conditional definition is now contained in a single `math` fence (previously
part of it rendered as raw LaTeX / a code block).
- De-Pythonized the `inexact_gradient_step!` docstring: a `# Throws` section now
documents the real `ErrorException`, with Julia string/list syntax.

### Removed
- Internal symbols are no longer exported into the public namespace: the global
counters (`Point_counter`, `Expression_counter`, `Function_counter`,
`Global_Constraint_counter`, `PSDMatrix_counter`, `NEXT_ID`) and the internal
helpers (`_is_already_evaluated_on_point`,
`_separate_leaf_functions_regarding_their_need_on_point`,
`_get_nb_eigs_and_corrected`). They remain reachable as `PEPit.<name>`.

## [0.1.1]

- Initial documented release: the core PEP workflow (`PEP`, `Point`,
`Expression`, `Constraint`, `PSDMatrix`), function and operator interpolation
classes, primitive steps, `solve!` / `solve_dual!`, and ~96 worked examples
validated against the Python `PEPit` package.
5 changes: 4 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "PEPit"
uuid = "362dabbc-d565-4ef8-96fa-aefabe1c072a"
version = "0.1.1"
version = "0.1.2"
authors = ["Shuvomoy Das Gupta <shuvomoy.dasgupta@gmail.com> and contributors"]

[deps]
Expand Down Expand Up @@ -33,3 +33,6 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

[workspace]
projects = ["docs"]
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# PEPit.jl

[![Documentation](https://img.shields.io/badge/Documentation-stable-purple.svg)](https://PerformanceEstimation.github.io/PEPit.jl/stable/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Release](https://img.shields.io/badge/Release-v0.1.2-blue.svg)](https://github.com/PerformanceEstimation/PEPit.jl/releases/tag/v0.1.2)

`PEPit.jl` is a native Julia implementation of the Performance Estimation Programming (PEP) methodology [1,2,3] and the Python package `PEPit` [4] for worst-case analysis of first-order optimization algorithms. The core idea in PEP is to model the design and analysis of first-order optimization algorithms as higher-level optimization problems called performance estimation problems (PEPs), which are semidefinite programs (SDPs). We then solve these SDPs numerically to obtain tight worst-case bounds for known algorithms and also to discover new algorithms under suitable conditions.

The intent of this Julia package is to be functionally equivalent to existing packages such as `PESTO` [5] and `PEPit` while providing a clean, Julia-native API along with a broader support of commercial and open-source solvers under the `JuMP` ecosystem [6].
Expand Down Expand Up @@ -139,7 +143,7 @@ After `solve_dual!`, `eval_dual(::Constraint)` and `eval_dual(::PSDMatrix)` are
Examples are standard Julia scripts. For instance, you can run them as:

```julia
include("examples/unconstrained_convex_optimization/gradient_exact_line_search.jl")
include("examples/unconstrained_convex_minimization/gradient_exact_line_search.jl")
```

## Notes and scope
Expand Down
8 changes: 8 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[deps]
Clarabel = "61c947e1-3e6d-4ee4-985a-eec8c727bd6e"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
PEPit = "362dabbc-d565-4ef8-96fa-aefabe1c072a"

[compat]
Documenter = "1.17"
158 changes: 158 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using Documenter
using PEPit

const IS_CI = get(ENV, "CI", "false") == "true"
const DOCS_ROOT = @__DIR__
const PACKAGE_ROOT = normpath(joinpath(DOCS_ROOT, ".."))
const EXAMPLES_ROOT = joinpath(PACKAGE_ROOT, "examples")
const GENERATED_EXAMPLES_ROOT = joinpath(DOCS_ROOT, "src", "generated_examples")
const REPO_EXAMPLES_URL = "https://github.com/PerformanceEstimation/PEPit.jl/blob/main/examples"

function _title_from_slug(slug::AbstractString)
return join(uppercasefirst.(split(replace(slug, "_" => " "))), " ")
end

function _extract_example_doc(path::AbstractString)
text = read(path, String)
m = match(r"(?s)@doc\s+raw\"\"\"(.*?)\"\"\"\s*function\s+(wc_[A-Za-z0-9_!]+)\s*\(", text)
m === nothing && return nothing
doc = replace(m.captures[1], r"^\n+" => "")
doc = replace(doc, r"\s+$" => "")
return (name = m.captures[2], doc = doc)
end

function _demote_markdown_headings(text::AbstractString; levels::Integer=1)
return replace(text, r"(?m)^#{1,4}\s+" => heading -> begin
first_non_hash = findfirst(!=('#'), heading)
level = first_non_hash === nothing ? length(heading) : first_non_hash - 1
repeat("#", level + levels) * heading[(level + 1):end]
end)
end

function _prepare_example_doc(text::AbstractString)
return _demote_markdown_headings(text; levels=1)
end

function _generate_example_doc_pages!()
isdir(GENERATED_EXAMPLES_ROOT) && rm(GENERATED_EXAMPLES_ROOT; recursive=true)
mkpath(GENERATED_EXAMPLES_ROOT)

repo_tree_base = replace(REPO_EXAMPLES_URL, "/blob/" => "/tree/")

# The Examples overview page is generated from the same scan as the per-example
# pages, so every family is listed uniformly and links never drift.
overview = IOBuffer()
println(overview, "# Examples")
println(overview)
println(overview, "The Julia examples are ordinary scripts under ",
"[`examples/`]($(repo_tree_base)), grouped by family. Each page below is ",
"generated from the corresponding `wc_*` function docstring and includes the ",
"problem statement, the algorithm, the performance metric, the reference ",
"guarantee when available, the Julia arguments and return values, and a link ",
"to the source file.")
println(overview)

pages = Pair{String,Any}[]
for family in sort(filter(isdir, readdir(EXAMPLES_ROOT; join=true)))
family_slug = basename(family)
family_title = _title_from_slug(family_slug)
entries = []

for path in sort(filter(p -> endswith(p, ".jl"), readdir(family; join=true)))
extracted = _extract_example_doc(path)
extracted === nothing && continue
example_slug = splitext(basename(path))[1]
example_title = _title_from_slug(example_slug)
rel_source = replace(relpath(path, EXAMPLES_ROOT), "\\" => "/")
push!(entries, (title=example_title,
slug=example_slug,
source_url="$(REPO_EXAMPLES_URL)/$(rel_source)",
doc=extracted.doc))
end

isempty(entries) && continue

family_output_root = joinpath(GENERATED_EXAMPLES_ROOT, family_slug)
mkpath(family_output_root)

family_pages = Pair{String,String}["Overview" => "generated_examples/$(family_slug)/index.md"]
open(joinpath(family_output_root, "index.md"), "w") do io
println(io, "# $(family_title)")
println(io)
println(io, "Examples in this category.")
println(io)
for entry in entries
println(io, "- [$(entry.title)]($(entry.slug).md)")
end
end

println(overview, "## $(family_title)")
println(overview)
println(overview, "[Source directory]($(repo_tree_base)/$(family_slug))")
println(overview)

for entry in entries
output_path = joinpath(family_output_root, "$(entry.slug).md")
open(output_path, "w") do io
println(io, "# $(entry.title)")
println(io)
println(io, "[Source file]($(entry.source_url))")
println(io)
println(io, _prepare_example_doc(entry.doc))
println(io)
end
push!(family_pages, entry.title => "generated_examples/$(family_slug)/$(entry.slug).md")
println(overview, "- [$(entry.title)](generated_examples/$(family_slug)/$(entry.slug).md)")
end
println(overview)

push!(pages, family_title => family_pages)
end

write(joinpath(DOCS_ROOT, "src", "examples.md"), String(take!(overview)))

return pages
end

const EXAMPLE_DOC_PAGES = _generate_example_doc_pages!()

# Mirror the root changelog into the docs as the "Release notes" page.
cp(joinpath(PACKAGE_ROOT, "CHANGELOG.md"), joinpath(DOCS_ROOT, "src", "changelog.md"); force=true)

makedocs(;
modules = [PEPit],
sitename = "PEPit.jl",
authors = "PEPit.jl contributors",
remotes = nothing,
linkcheck = false,
checkdocs = :exports,
warnonly = [:missing_docs],
pagesonly = true,
meta = Dict(:CurrentModule => PEPit),
format = Documenter.HTML(;
prettyurls = IS_CI,
edit_link = "main",
repolink = "https://github.com/PerformanceEstimation/PEPit.jl",
assets = ["assets/pepit.css"],
),
pages = [
"Home" => "index.md",
"Quick start" => "quickstart.md",
"API reference" => [
"Core workflow" => "api/core.md",
"Function classes" => "api/functions.md",
"Operator classes" => "api/operators.md",
"Primitive steps" => "api/steps.md",
"Utilities" => "api/utilities.md",
],
"Examples" => vcat(["Overview" => "examples.md"], EXAMPLE_DOC_PAGES),
"Tutorials" => "tutorials.md",
"Contributing" => "contributing.md",
"Release notes" => "changelog.md",
],
)

deploydocs(;
repo = "github.com/PerformanceEstimation/PEPit.jl.git",
devbranch = "main",
)
49 changes: 49 additions & 0 deletions docs/src/api/core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Core workflow

```@meta
CurrentModule = PEPit
```

## Main objects

```@docs
PEP
Point
Expression
Constraint
PSDMatrix
BlockPartition
PEPFunction
DualPEPCertificate
```

## Problem construction

```@docs
declare_function!
declare_block_partition!
set_initial_point!
set_initial_condition!
set_performance_metric!
add_constraint!
add_psd_matrix!
```

## Oracles and fixed points

```@docs
oracle!
gradient!
value!
stationary_point!
fixed_point!
```

## Solving and evaluation

```@docs
solve!
solve_dual!
evaluate
eval_dual
```
29 changes: 29 additions & 0 deletions docs/src/api/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Function classes

```@meta
CurrentModule = PEPit
```

The following exported types model interpolation constraints for common
classes of scalar functions. Parameters are supplied through an
`OrderedDict`, matching the examples in `PEPit.jl/examples`.

```@docs
AbstractFunction
ConvexFunction
ConvexLipschitzFunction
SmoothFunction
SmoothConvexFunction
SmoothStronglyConvexFunction
StronglyConvexFunction
ConvexIndicatorFunction
ConvexQGFunction
ConvexSupportFunction
RsiEbFunction
SmoothConvexLipschitzFunction
SmoothStronglyConvexQuadraticFunction
SmoothQuadraticLojasiewiczFunctionCheap
SmoothQuadraticLojasiewiczFunctionExpensive
BlockSmoothConvexFunctionCheap
BlockSmoothConvexFunctionExpensive
```
Loading
Loading