Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Binary file added .DS_Store
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR

Binary file not shown.
162 changes: 162 additions & 0 deletions .claude/skills/find-bib/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
name: find-bib
description: Find or construct a BibTeX reference for an OptimizationProblems.jl meta file. Given a DOI, problem name, or free-form reference text, searches for the DOI online, fetches BibTeX from doi2bib.org, and formats the result as a Julia raw string ready to paste into src/Meta/<name>.jl.
argument-hint: <problem-name | DOI | "free-form reference text">
allowed-tools: [Read, Glob, Grep, WebSearch, WebFetch]
---

# find-bib

Find or construct a BibTeX reference and format it as a Julia raw string for `src/Meta/<name>.jl`.

## Arguments

The user invoked this skill with: $ARGUMENTS

This can be:
- A **problem name** from `src/Meta/` (e.g. `zangwil3`, `hs1`) — skill will read the file and extract context
- A **DOI** (e.g. `10.1145/355934.355936` or `https://doi.org/10.1145/355934.355356`)
- **Free-form reference text** (title, authors, journal, year — quoted or unquoted)
- **Empty** — if no argument is given, ask the user to specify a problem name or reference

---

## Instructions

Follow these steps in order.

### Step 1 — Gather source material

If `$ARGUMENTS` looks like a problem name (no spaces, no slashes, exists as `src/Meta/<name>.jl`):
- Read `src/Meta/$ARGUMENTS.jl`
- Extract `:url`, `:notes`, `:origin_notes`, and the existing `:reference` field (may be empty)
- Use this text as the source for Steps 2–4

Otherwise, treat `$ARGUMENTS` directly as a DOI or free-form reference text.

### Step 2 — Extract or find a DOI

1. Regex-scan all gathered text for a DOI pattern: `10\.\d{4,}/\S+`
- DOIs appear in `:url`, `:notes`, `:origin_notes`, or in the argument itself
2. If no DOI is found, run a **WebSearch** to locate one:
- Try: `"<title or key terms>" "<first author>" DOI`
- Try: `site:doi.org "<title or key terms>"`
- Try CrossRef: `site:search.crossref.org "<title or key terms>"`
3. Extract the DOI string from any search result that contains one.

### Step 3 — Fetch BibTeX

If a DOI was found (say `10.1145/355934.355936`), try these endpoints in order:

1. **CrossRef** (primary): `https://api.crossref.org/works/10.1145/355934.355936/transform/application/x-bibtex`
2. **doi2bib.org** (fallback): `https://www.doi2bib.org/bib/10.1145/355934.355936`

The response is plain-text BibTeX — clean up whitespace if needed, then apply these normalizations:

- **Citation key**: CrossRef often returns auto-generated keys like `More_1981` or `more1981testingunconstrained`. Always rename the key to the `Author1Author2YYYY` format described in Step 4 (e.g. `MoreGarbowHillstrom1981`).
- **`pages` field**: normalize to BibTeX double-hyphen. Replace Unicode en-dash `–` or a single hyphen `-` between page numbers with `--` (e.g. `175--184`).

If both endpoints fail or return an error, fall through to Step 4.

### Step 4 — Construct BibTeX manually (fallback)

If no DOI was found or doi2bib.org failed, construct the best possible BibTeX from all available information.

Choose the entry type:

| Type | Use when |
|---|---|
| `@article` | journal or conference paper |
| `@book` | book or edited volume |
| `@techreport` | institutional or technical report |
| `@misc` | dataset, software, website, or unclear |

**Citation key format:** `Author1Author2YYYY` using last names only (e.g. `MoreGarbowHillstrom1981`, `HockSchittkowski1981`). For a single author: `AuthorYYYY`. For institutional authors use a compact CamelCase form (e.g. `NISTStRD`).

**`pages` field:** always use double-hyphen: `175--184`. Convert Unicode en-dash `–`, em-dash `—`, or single hyphen `-` between page numbers to `--`.

**LaTeX encoding for special characters:** `Mor{\'e}`, `{\'E}`, etc.

**Flag uncertain fields** with a trailing `% UNVERIFIED` comment on that line.

### Step 5 — Present the result

Show three things:

**1. Status line** — one sentence: whether the DOI was found, and from where.

**2. The BibTeX entry** in a code block:
```bibtex
@article{AuthorYear,
author = {Last, First and Last2, First2},
title = {Title of the Article},
journal = {Journal Name},
year = {YYYY},
volume = {V},
number = {N},
pages = {P1--P2},
doi = {10.xxxx/xxxxx}
}
```

**3. The Julia snippet** ready to paste into `src/Meta/<name>.jl`:
```julia
:reference => raw"""
@article{AuthorYear,
author = {Last, First and Last2, First2},
title = {Title of the Article},
journal = {Journal Name},
year = {YYYY},
volume = {V},
number = {N},
pages = {P1--P2},
doi = {10.xxxx/xxxxx}
}
""",
```

### Step 6 — Suggest next steps

- List any fields marked `% UNVERIFIED` that the user should check manually.
- If a DOI was found and the file's `:url` field does not already contain it, suggest adding `https://doi.org/<DOI>` to `:url`. The `:url` field supports multiple URLs as a **comma-separated string**. If a URL is already present, append the new one: `"https://existing.url, https://doi.org/<DOI>"`.
- If the problem name was given, name the exact file to edit: `src/Meta/<name>.jl`.

---

## BibTeX type templates (reference)

```bibtex
@article{AuthorYear,
author = {Last, First},
title = {Title},
journal = {Journal},
year = {YYYY},
volume = {V},
number = {N},
pages = {P1--P2},
doi = {10.xxxx/xxxxx}
}

@book{AuthorYear,
author = {Last, First},
title = {Book Title},
publisher = {Publisher},
address = {City},
year = {YYYY}
}

@techreport{AuthorYear,
author = {Last, First},
title = {Report Title},
institution = {Institution},
number = {Report Number},
year = {YYYY}
}

@misc{AuthorYear,
author = {Last, First},
title = {Title},
year = {YYYY},
howpublished = {\url{https://...}}
}
```
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
*.jl.*.cov
*.jl.mem
Manifest.toml
.DS_Store
*.bib
*/settings.local.json
.DS_Store
201 changes: 201 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# CLAUDE.md — OptimizationProblems.jl

## Overview

**OptimizationProblems.jl** (v0.9.3) is a Julia package providing ~680+ nonlinear programming (NLP) test problems for benchmarking and developing optimization solvers. It is part of the [JuliaSmoothOptimizers](https://github.com/JuliaSmoothOptimizers) ecosystem and integrates with NLPModels, ADNLPModels, and JuMP.

**Central registry:** `OptimizationProblems.meta` is a `DataFrame` built at load time from `src/Meta/`. It gives instant access to metadata (nvar, ncon, objtype, origin, bounds, feasibility, …) for all problems **without instantiating any model** — extremely useful for filtering and analysis.

```julia
using OptimizationProblems
# Filter unconstrained problems with ≤ 50 variables
df = OptimizationProblems.meta
df[(df.ncon .== 0) .& (df.nvar .≤ 50), :]
```

---

## Repository Structure

```
src/
OptimizationProblems.jl # Main module — builds meta DataFrame
ADNLPProblems/
ADNLPProblems.jl # Module loader (lazy, requires ADNLPModels)
<name>.jl # One file per problem
PureJuMP/
PureJuMP.jl # Module loader (always loaded)
<name>.jl # One file per problem
Meta/
<name>.jl # One metadata file per problem
test/ # Julia unit test suite
docs/ # Documenter.jl docs (make.jl, src/)
src/contributing.md # Authoritative contributor guidelines
benchmark/ # BenchmarkTools suite (own Project.toml)
data/ # .jld2 data files for mesh-heavy problems
```

---

## Three-File Pattern

New problems should provide exactly three files sharing the same base name. (Some existing problems are JuMP-only and lack an `ADNLPProblems` file; a few files also define multiple problems — see `triangle.jl` — but these are legacy exceptions.)

### `src/ADNLPProblems/<name>.jl`

AD-based model. Lazy-loaded (only available when `ADNLPModels` is imported).

```julia
export <name>

function <name>(; n::Int = default_nvar, type::Type{T} = Float64, kwargs...) where {T}
# x0 must be Vector{T}, objective must return type T
f(x) = ...
x0 = ones(T, n)
return ADNLPModels.ADNLPModel(f, x0, name = "<name>"; kwargs...)
end
```

### `src/PureJuMP/<name>.jl`

JuMP algebraic model. Always loaded. Short docstring above the function.

```julia
export <name>

"Short description of the problem."
function <name>(args...; n::Int = default_nvar, kwargs...)
nlp = Model()
# define variables, constraints, objective
return nlp
end
```

### `src/Meta/<name>.jl`

Metadata dictionary. Does **not** export the problem function.

```julia
<name>_meta = Dict(
:nvar => 10,
:variable_nvar => false,
:ncon => 0,
:variable_ncon => false,
:minimize => true,
:name => "<name>",
:has_equalities_only => false,
:has_inequalities_only => false,
:has_bounds => false,
:has_fixed_variables => false,
:objtype => :other, # see valid values below
:contype => :unconstrained, # see valid values below
:best_known_lower_bound => -Inf,
:best_known_upper_bound => Inf,
:is_feasible => true,
:defined_everywhere => true,
:origin => :academic, # see valid values below
# Additional fields (branch move-docstring-to-metadata):
:url => "https://...", # must match ^https?://
:notes => raw"""""", # problem description
:origin_notes => raw"""""", # provenance
:reference => raw"""@type{key, ...}""", # BibTeX, balanced braces
:lib => "CUTEst:NAME", # library codes, comma-separated
)
```

**Valid values:**
- `:objtype`: `:none`, `:constant`, `:linear`, `:quadratic`, `:sum_of_squares`, `:other`, `:least_squares`
- `:contype`: `:unconstrained`, `:linear`, `:quadratic`, `:general`
- `:origin`: `:academic`, `:modelling`, `:real`, `:unknown`

---

## Scalable Problems

Problems that accept a variable size use `n::Int = default_nvar` (default = 100).

- Adjust invalid `n` to the closest valid value silently with `@warn`, never throw an error. The exact adjustment depends on the problem's constraints on `n` (minimum size, divisibility, parity, etc.).
- Export getter functions for the Meta file: `get_<name>_nvar`, `get_<name>_ncon`, `get_<name>_nlin`, `get_<name>_nnln`, `get_<name>_nequ`, `get_<name>_nineq`
- Reference: `src/ADNLPProblems/arglina.jl`, `src/PureJuMP/arglina.jl`, `src/Meta/arglina.jl`

---

## Nonlinear Least-Squares (NLS) Problems

- Set `:objtype => :least_squares` in the meta file
- Support `use_nls=true/false` keyword: returns `ADNLSModel` or `ADNLPModel`
- Export `get_<name>_nls_nequ`
- `residual!(nls, x, Fx)` must be allocation-free
- Reference: `src/ADNLPProblems/lanczos1.jl`, `src/ADNLPProblems/brownal.jl`

---

## Code Formatting

Uses [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl). Config is in `.JuliaFormatter.toml`:

| Setting | Value |
|---------|-------|
| `margin` | 100 |
| `indent` | 2 |
| `normalize_line_endings` | `"unix"` |

Format the codebase locally:

```julia
using JuliaFormatter
format(".")
```

---

## Testing

The test suite uses Julia's `Test` stdlib with `Distributed` for parallel execution.

```
julia --project test/runtests.jl
```

Key test files:

| File | Purpose |
|------|---------|
| `test/test-defined-problems.jl` | Verifies all meta entries have working implementations |
| `test/test-scalable.jl` | Validates scalable problem sizing and getter formulas |
| `test/test-in-place-residual.jl` | Allocation checks for NLS residuals |
| `test/utils.jl` | Helpers: `generate_meta()`, `test_multi_precision()` |

**Always test:**
- Multiple sizes for scalable problems: `n = 5`, `n = default_nvar`, large `n`
- Both `Float32` and `Float64`
- Allocation-free in-place APIs (`cons_nln!`, `residual!`)

---

## Good Reference Problems

| Category | Examples |
|----------|---------|
| Unconstrained, scalable | `arwhead`, `arglina` |
| Constrained | `hs100`, `catmix` |
| Least squares (NLS) | `lanczos1`, `brownal` |
| With data files | `catmix`, `rocket` |

---

## Benchmarks

The `benchmark/` directory has its own Julia environment. Run locally:

```
julia benchmark/run_local.jl
```

Benchmarks cover constructor time and objective evaluation for both `ADNLPProblems` and `PureJuMP`, using `BenchmarkTools.jl`.

---

## Full Contributor Guidelines

See [`docs/src/contributing.md`](docs/src/contributing.md) for the complete checklist when adding or modifying problems, including the full reviewer checklist for meta fields, implementation consistency, type stability, and allocation requirements.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ NLPModelsJuMP = "792afdf1-32c1-5681-94e0-d7bf7a5df49e"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ADNLPModels", "Distributed", "NLPModels", "NLPModelsJuMP", "Test"]
test = ["ADNLPModels", "Distributed", "NLPModels", "NLPModelsJuMP", "Test"]
Loading