Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f3c5d24
Merge pull request #7 from apoorvalal/fw_simplex
apoorvalal Nov 18, 2024
6c4daa3
add unit tests for MLE
apoorvalal Dec 18, 2024
e615fcd
Refactor C++ code into modular files and switch to setuptools build s…
apoorvalal Mar 16, 2025
99efd18
Add comprehensive benchmarking script
apoorvalal Mar 16, 2025
5159f1a
"add full benchmarks"
apoorvalal Mar 16, 2025
2ff25ee
version bump; figure in readme
apoorvalal Mar 16, 2025
ec914ce
Merge pull request #9 from apoorvalal/modular-cpp-structure
apoorvalal Mar 16, 2025
06b3864
Update GitHub Actions to use upload-artifact v4
apoorvalal Mar 16, 2025
182263c
"prelim gmm implementation"
apoorvalal Mar 16, 2025
8be1039
clean up GMM, implement bootstrap, example.
apoorvalal Mar 23, 2025
c3bbf7b
cleanup
apoorvalal Mar 23, 2025
450196c
add tests
apoorvalal Mar 23, 2025
d7af935
version bump
apoorvalal Mar 23, 2025
8380c22
Merge pull request #10 from apoorvalal/gmm
apoorvalal Mar 23, 2025
644b058
changelog generator
apoorvalal Mar 23, 2025
8190b43
version bump- this time its bumpier
apoorvalal Mar 23, 2025
4445066
Bump version to 0.2.1 to resolve PyPI filename conflict
apoorvalal Mar 23, 2025
27d6ae5
Fix wheel building in CI by removing continue-on-error flag
apoorvalal Mar 23, 2025
a617f0f
Bump version to 0.2.2
apoorvalal Mar 23, 2025
7a6b953
Fix packaging conflicts between setup.py and pyproject.toml for versi…
apoorvalal Mar 23, 2025
fd2db7f
Fix Ensmallen header discovery during wheel building
apoorvalal Mar 23, 2025
d2aad73
"revert to last build setup that got wheels working"
apoorvalal Mar 24, 2025
5979c8d
switch back to meson build
apoorvalal Mar 24, 2025
3540bb0
"fix unit test action"
apoorvalal Mar 24, 2025
f513082
"try uv in unit test action"
apoorvalal Mar 24, 2025
1d3888e
"local tests for now"
apoorvalal Mar 24, 2025
69f1f80
"add paper directory"
apoorvalal Mar 30, 2025
e5c23cf
"clean up results dir"
Apr 1, 2025
1c895f5
Merge pull request #12 from apoorvalal/joss
apoorvalal Apr 1, 2025
022d98e
fix image path in readme
apoorvalal Apr 8, 2025
13f23ab
fix gmm notebook
apoorvalal Apr 14, 2025
e4ae933
"fix to export simplex constrained FW again - need for synth"
apoorvalal May 18, 2025
80f400f
"version bump"
apoorvalal May 21, 2025
a2d93a4
add PyReport class and expose it
gauravmanmode May 22, 2025
4713d6c
Merge branch 'master' into master
gauravmanmode Jul 9, 2025
22c08fa
Merge branch 'master' into master
gauravmanmode Aug 12, 2025
171a920
add tests
gauravmanmode Aug 12, 2025
bbdaa53
add notebook
gauravmanmode Aug 12, 2025
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
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ py.install_sources(
],
pure: false,
subdir: 'pyensmallen'
)
)
2 changes: 1 addition & 1 deletion notebooks/gmm.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -526,4 +526,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
226 changes: 226 additions & 0 deletions notebooks/report.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "e6898559",
"metadata": {},
"source": [
"# How to get more info about the optimization run\n",
"\n",
"The `report` parameter is optional when initializing the optimizer.\n",
"For detailed run information, provide a Report callback object to the optimizer. \n",
"This is done by passing an instance of the Report callback class during initialization."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "f6b0aef6",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pyensmallen as pye\n",
"from pprint import pprint"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "25fc7777",
"metadata": {},
"outputs": [],
"source": [
"def square(x):\n",
" return x@x\n",
"\n",
"def objective_function(x, grad):\n",
" grad[:] = 2*x\n",
" return square(x)\n",
" "
]
},
{
"cell_type": "markdown",
"id": "a3ad6515",
"metadata": {},
"source": [
"It is optional to get a more detailed optimization report.\n",
"You can run the optimizer normally."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "1eeb4af0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[-1.13686838e-13 1.13686838e-13]\n"
]
}
],
"source": [
"# Initial guess\n",
"initial_x = np.array([-1000, 1000.0])\n",
"\n",
"# Initialize L-BFGS optimizer\n",
"optimizer = pye.L_BFGS()\n",
"\n",
"# Optimize\n",
"result = optimizer.optimize(objective_function, initial_x)\n",
"\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"id": "407d1a4c",
"metadata": {},
"source": [
"## Getting more info about the optimization\n",
"Passing this callback object allows to retrieve more info about the optimization run."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "e9b998dc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[-1.13686838e-13 1.13686838e-13]\n",
"{'evaluate_calls': 10,\n",
" 'gradient_calls': 10,\n",
" 'iterations': 2,\n",
" 'objective_value': 2.5849394142282115e-26,\n",
" 'total_time': 3.4675e-05}\n"
]
}
],
"source": [
"\n",
"# Initialize L-BFGS optimizer\n",
"optimizer = pye.L_BFGS()\n",
"\n",
"# Initialize a dict to store optimization run info \n",
"info = dict()\n",
"\n",
"# Initialize Report callback object \n",
"report = pye.Report(info, disableOutput=True)\n",
"\n",
"# Initialize L-BFGS optimizer passing a Report object\n",
"result = optimizer.optimize(objective_function, initial_x, report=report)\n",
"print(result)\n",
"pprint(info)"
]
},
{
"cell_type": "markdown",
"id": "5ef84c09",
"metadata": {},
"source": [
"By setting `disableOutput=False`, callback prints a optimizer report to stdout which shows various info about the optimization run."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "09f97e11",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Modified Optimization Report\n",
"--------------------------------------------------------------------------------\n",
"\n",
"Initial Coordinates:\n",
" -1.0000e+03 1.0000e+03\n",
"\n",
"Final coordinates:\n",
" -1.1369e-13 1.1369e-13\n",
"\n",
"iter loss loss change |gradient| total time \n",
"0 1.52e+06 0 2.47e+03 2.87e-05 \n",
"1 2.58e-26 1.52e+06 3.22e-13 3.44e-05 \n",
"\n",
"--------------------------------------------------------------------------------\n",
"\n",
"Version:\n",
"ensmallen: 2.22.1 (E-Bike Excitement)\n",
"armadillo: 12.6.7 (Cortisol Retox)\n",
"\n",
"Function:\n",
"Number of functions: 1\n",
"Coordinates rows: 2\n",
"Coordinates columns: 1\n",
"\n",
"Loss:\n",
"Initial 1.52e+06\n",
"Final 2.58e-26\n",
"Change 1.52e+06\n",
"\n",
"Optimizer:\n",
"Maximum iterations: 10000\n",
"Reached maximum iterations: false\n",
"Iterations: 2\n",
"Coordinates max. norm: 2.47e+03\n",
"Evaluate calls: 10\n",
"Gradient calls: 10\n",
"Time (in seconds): 3.44e-05\n"
]
}
],
"source": [
"# Initialize L-BFGS optimizer\n",
"optimizer = pye.L_BFGS()\n",
"\n",
"# Initialize a dict to store optimization run info \n",
"info = dict()\n",
"\n",
"# Initialize Report callback object \n",
"report = pye.Report(info, disableOutput=False)\n",
"\n",
"# Initialize L-BFGS optimizer passing a Report object\n",
"result = optimizer.optimize(objective_function, initial_x, report=report)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2da4dbe3",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "optimagic",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.18"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 1 addition & 1 deletion pyensmallen/gmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,4 +549,4 @@ def _jit_objective(self, beta, z, y, x, W):
def _jit_mean_moment_fn(self, beta, z, y, x):
"""JIT-compiled mean of moment conditions"""
moments = self._moment_cond_jax(z, y, x, beta)
return jnp.mean(moments, axis=0)
return jnp.mean(moments, axis=0)
22 changes: 21 additions & 1 deletion pyensmallen/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
#include "constrained.hpp"
#include "first_order.hpp"

#include "report.hpp"

namespace py = pybind11;

PYBIND11_MODULE(_pyensmallen, m)
{

// L-BFGS (Newton-type) optimizer
py::class_<PyL_BFGS>(m, "L_BFGS")
.def(py::init<>())
Expand Down Expand Up @@ -40,7 +43,11 @@ PYBIND11_MODULE(_pyensmallen, m)
&PyL_BFGS::setMaxLineSearchTrials)
.def_property("minStep", &PyL_BFGS::getMinStep, &PyL_BFGS::setMinStep)
.def_property("maxStep", &PyL_BFGS::getMaxStep, &PyL_BFGS::setMaxStep)
.def("optimize", &PyL_BFGS::Optimize);
.def("optimize", &PyL_BFGS::Optimize,
py::arg("f"),
py::arg("initial_point"),
py::arg("report") = nullptr);


// FrankWolfe - constrained optimization
py::class_<PyFrankWolfe>(m, "FrankWolfe")
Expand Down Expand Up @@ -245,4 +252,17 @@ PYBIND11_MODULE(_pyensmallen, m)
&PyAdamType<ens::NadamUpdate>::getResetPolicy,
&PyAdamType<ens::NadamUpdate>::setResetPolicy)
.def("optimize", &PyAdamType<ens::NadamUpdate>::Optimize);

py::class_<ens::PyReport>(m, "Report")
.def(py::init<
py::dict,
bool,
double,
size_t>(),
py::arg("resultIn"),
py::arg("disableOutput") = false,
py::arg("iterationsPercentageIn") = 0.1,
py::arg("outputMatrixSizeIn") = 4
);

}
29 changes: 16 additions & 13 deletions pyensmallen/newton_type.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "utils.hpp"
#include "report.hpp"

// Wrapper for L-BFGS optimizer
class PyL_BFGS
Expand Down Expand Up @@ -79,19 +80,21 @@ class PyL_BFGS
void setMaxStep(double maxStep) { optimizer.MaxStep() = maxStep; }

py::array_t<double> Optimize(DifferentiableFunction f,
py::array_t<double> initial_point)
{
py::buffer_info buf_info = initial_point.request();
arma::vec arma_initial_point(static_cast<double *>(buf_info.ptr),
buf_info.shape[0], false, true);

DifferentiableFunctionWrapper fw(f);
arma::vec result = arma_initial_point;

optimizer.Optimize(fw, result);

return py::array_t<double>(result.n_elem, result.memptr());
}
py::array_t<double> initial_point, ens::PyReport* report = nullptr)
{
py::buffer_info buf_info = initial_point.request();
arma::vec arma_initial_point(static_cast<double *>(buf_info.ptr),
buf_info.shape[0], false, true);

DifferentiableFunctionWrapper fw(f);
arma::vec result = arma_initial_point;
if (report)
optimizer.Optimize(fw, result, *report);
else
optimizer.Optimize(fw, result);
return py::array_t<double>(result.n_elem, result.memptr());

}

private:
ens::L_BFGS optimizer;
Expand Down
Loading