Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH add Lasso.jl julia solver #89

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3232c37
Fix bug in glmnet solver
jolars Nov 30, 2021
df67ce6
add debug script
mathurinm Dec 1, 2021
6aa76de
add computation of objective functions
mathurinm Dec 1, 2021
47ed265
test that for lambda just below lambda max both solutions are non zero
mathurinm Dec 1, 2021
4d24648
Drop unnecessary computation of lambda_max
jolars Dec 1, 2021
fed8fa4
Harmonize lambda scaling for glmnet solver
jolars Dec 1, 2021
93f5a40
Merge branch 'main' of github.com:jolars/benchmark_lasso
jolars Dec 1, 2021
790607a
Switch benchopt method to use tolerance instead
jolars Dec 6, 2021
8f947d8
tweaks to test_glmnet
mathurinm Dec 6, 2021
1be208a
change glmnet criterion to use a higher patience
mathurinm Dec 6, 2021
46f6f04
faster tol decrease for glmnet, try to have common initial point
mathurinm Dec 7, 2021
68a379c
increase patience again
mathurinm Dec 7, 2021
c57994d
get common starting point, comment code
mathurinm Dec 8, 2021
43723de
CLN remove test_glmnet.py
mathurinm Dec 8, 2021
5fa72e9
more comments on glmnet behavior
mathurinm Dec 8, 2021
96e5292
Update solvers/glmnet.py
tomMoral Dec 9, 2021
f182a0b
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars Apr 12, 2022
749dacf
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars Apr 22, 2022
e9da0b3
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 11, 2022
ec88f65
ENH add Lasso.jl Julia solver
jolars May 11, 2022
bf09186
FIX remove unnecessary using calls
jolars May 11, 2022
dc0d5be
FIX remove unnecessary dependencies
jolars May 11, 2022
b60f905
fix: set randomize and stopearly to defaults
jolars May 11, 2022
653db19
Merge branch 'lassojl-solver' of github.com:jolars/benchmark_lasso in…
jolars May 11, 2022
406b071
fix: run tol == INFINITY iteration on julia side
jolars May 11, 2022
b48933f
fix: decrease tolerance used for JIT compilation
jolars May 11, 2022
77ac79f
fix: catch convergence errors and return prev sol
jolars May 12, 2022
a721f82
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 12, 2022
04d400b
fix(style): resolve linter warnings
jolars May 12, 2022
f05cfc8
fix(ci): fix julia error by disabling multithreads
jolars May 12, 2022
19b99cb
Revert "fix(ci): fix julia error by disabling multithreads"
jolars May 12, 2022
704f1ea
fix(ci): try to fix failing MacOS julia tests
jolars May 12, 2022
b4f3c2a
fix(ci): disable julia multi-threading on osx
jolars May 12, 2022
1a2f1f6
fix(ci): move JULIA_NUM_THREADS export to Test run
jolars May 12, 2022
a5346f1
fix(ci): stop testing on OSX
jolars May 13, 2022
1e97f76
Merge branch 'main' of github.com:benchopt/benchmark_lasso into lasso…
mathurinm May 17, 2022
4f09c92
imports in import_ctx
mathurinm May 17, 2022
5670ac5
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 17, 2022
227a574
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 18, 2022
7e4f8b2
Merge branch 'main' of github.com:jolars/benchmark_lasso into lassojl…
jolars May 18, 2022
b50db11
FIX ignore FutureWarnings from pyjulia
jolars May 18, 2022
9661be3
FIX shorten line to appease flake8
jolars May 18, 2022
fad0d0c
FIX remove trailing whitespace
jolars May 18, 2022
3d5c70f
Merge branch 'main' of github.com:jolars/benchmark_lasso into lassojl…
jolars May 18, 2022
336f607
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 20, 2022
fc4e4ce
Merge branch 'main' of github.com:benchopt/benchmark_lasso
jolars May 23, 2022
2486c6b
Merge branch 'main' of github.com:jolars/benchmark_lasso into lassojl…
jolars May 23, 2022
acb003f
Merge branch 'main' of github.com:jolars/benchmark_lasso into lassojl…
jolars May 23, 2022
ed18ec9
refactor: increase the maximum number of iters
jolars Jun 20, 2022
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
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,15 @@ jobs:
branch=${BENCHOPT_BRANCH##*:}
pip install -U git+https://github.com/$user/benchopt@$branch

- name: Disable Julia multi-threading on MacOS
if: matrix.os == 'macos-latest'
run: |
export JULIA_NUM_THREADS=1

- name: Test
run: |
export OMP_NUM_THREADS=1
export JULIA_NUM_THREADS=1
benchopt test . --env-name bench_test_env -vl
benchopt test . --env-name bench_test_env -vl --skip-install

Expand Down
51 changes: 51 additions & 0 deletions solvers/lasso_jl.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Lasso
using PyCall
using SparseArrays

function scipyCSC_to_julia(A)
m, n = A.shape
colPtr = Int[i + 1 for i in PyArray(A."indptr")]
rowVal = Int[i + 1 for i in PyArray(A."indices")]
nzVal = Vector{Float64}(PyArray(A."data"))
B = SparseMatrixCSC{Float64,Int}(m, n, colPtr, rowVal, nzVal)

return B
end

function solve_lasso(
X,
y::Vector{Float64},
lambda::Vector{Float64},
fit_intercept::Bool,
tol::Float64,
cd_maxiter::Int,
get_null_solution::Bool,
)
p = size(X, 2)

w = if fit_intercept zeros(Float64, p + 1) else zeros(Float64, p) end

converged = true

# TODO(jolars): once https://github.com/JuliaStats/Lasso.jl/issues/70 or
# maybe https://github.com/JuliaStats/Lasso.jl/issues/71 is/are resolved,
# we should not need the try-catch here
if !get_null_solution
try
lasso_fit = fit(
LassoPath,
X,
y;
λ=lambda, standardize=false, intercept=fit_intercept,
maxncoef=max(size(X, 1), size(X, 2)) * 100,
cd_maxiter=cd_maxiter,
cd_tol=tol,
)
w = coef(lasso_fit)
catch
converged = false
end
end

return w, converged
end
89 changes: 89 additions & 0 deletions solvers/lasso_jl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from benchopt import safe_import_context
from benchopt.helpers.julia import (
JuliaSolver,
assert_julia_installed,
get_jl_interpreter,
)
from benchopt.runner import INFINITY
from benchopt.stopping_criterion import SufficientProgressCriterion

with safe_import_context() as import_ctx:
assert_julia_installed()
import warnings
import numpy as np
from scipy import sparse

from pathlib import Path
# File containing the function to be called from julia
JULIA_SOLVER_FILE = str(Path(__file__).with_suffix(".jl"))


class Solver(JuliaSolver):
name = "lasso_jl"
stopping_criterion = SufficientProgressCriterion(
patience=7, eps=1e-15, strategy="tolerance"
)
julia_requirements = [
"Lasso",
"PyCall",
"SparseArrays",
]
references = [
'J. Friedman, T. J. Hastie and R. Tibshirani, "Regularization paths '
'for generalized linear models via coordinate descent", '
"J. Stat. Softw., vol. 33, no. 1, pp. 1-22, NIH Public Access (2010)"
]

def set_objective(self, X, y, lmbd, fit_intercept):
self.n, self.p = X.shape
self.X = X
self.y = y
self.lmbd = np.array([lmbd])
self.fit_intercept = fit_intercept

jl = get_jl_interpreter()
jl.include(str(JULIA_SOLVER_FILE))
self.solve_lasso = jl.solve_lasso

if sparse.issparse(X):
scipyCSC_to_julia = jl.pyfunctionret(
jl.scipyCSC_to_julia, jl.Any, jl.PyObject
)
self.X = scipyCSC_to_julia(X)

# Trigger Julia JIT compilation
w_dim = self.p + 1 if fit_intercept else self.p
self.prev_solution = np.zeros(w_dim)

warnings.filterwarnings("ignore", category=FutureWarning)
self.run(1e-2)

def run(self, tol):
# remove possibly spurious warnings from pyjulia
# TODO: remove filter when
# https://github.com/JuliaPy/pyjulia/issues/497 is resolved or
# otherwise fix the warning
warnings.filterwarnings("ignore", category=FutureWarning)

coefs, converged = self.solve_lasso(
self.X,
self.y,
self.lmbd / len(self.y),
self.fit_intercept,
tol**1.8,
1_000_000,
tol == INFINITY,
)
if converged:
self.coefs = coefs
self.prev_solution = coefs
else:
self.coefs = self.prev_solution

def get_result(self):
coefs = np.ravel(self.coefs)

if self.fit_intercept:
coefs = np.hstack((coefs[1:], coefs[0]))

return coefs
4 changes: 4 additions & 0 deletions test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def check_test_solver_install(solver_class):
if 'julia' in solver_class.name.lower():
pytest.xfail('Julia install from conda fails currently.')

# Julia segfauls on OSX
if solver_class.name.lower() == 'lasso_jl' and sys.platform == 'darwin':
pytest.xfail('Julia solvers segfauls on OSX.')

# ModOpt install change numpy version, breaking celer install.
# See CEA-COSMIC/ModOpt#144. Skipping for now
if ('modopt' in solver_class.name.lower()):
Expand Down