diff --git a/.github/workflows/install_learnware_with_pip.yaml b/.github/workflows/install_learnware_with_pip.yaml index e5e9ed2..350ed1d 100644 --- a/.github/workflows/install_learnware_with_pip.yaml +++ b/.github/workflows/install_learnware_with_pip.yaml @@ -39,11 +39,6 @@ jobs: conda run -n learnware python -m pip install --upgrade pip conda run -n learnware python -m pip install pytest - - name: Install faiss for MacOS - if: ${{ matrix.os == 'macos-11' || matrix.os == 'macos-latest' }} - run: | - conda run -n learnware conda install -c pytorch faiss - - name: Install learnware run: | conda run -n learnware python -m pip install learnware diff --git a/.github/workflows/install_learnware_with_source.yaml b/.github/workflows/install_learnware_with_source.yaml index 1702f6b..02cba9e 100644 --- a/.github/workflows/install_learnware_with_source.yaml +++ b/.github/workflows/install_learnware_with_source.yaml @@ -44,11 +44,6 @@ jobs: # stop the build if there are Python syntax errors or undefined names conda run -n learnware python -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - - name: Install faiss for MacOS - if: ${{ matrix.os == 'macos-11' || matrix.os == 'macos-latest' }} - run: | - conda run -n learnware conda install -c pytorch faiss - - name: Install learnware run: | conda run -n learnware python -m pip install . diff --git a/examples/dataset_m5_workflow/example_init.py b/examples/dataset_m5_workflow/example_init.py index e0aabdd..eade812 100644 --- a/examples/dataset_m5_workflow/example_init.py +++ b/examples/dataset_m5_workflow/example_init.py @@ -7,7 +7,7 @@ from learnware.model import BaseModel class Model(BaseModel): def __init__(self): - super(Model, self).__init__(input_shape=(82,), output_shape=()) + super(Model, self).__init__(input_shape=(82,), output_shape=(1,)) dir_path = os.path.dirname(os.path.abspath(__file__)) self.model = lgb.Booster(model_file=os.path.join(dir_path, "model.out")) diff --git a/examples/dataset_m5_workflow/main.py b/examples/dataset_m5_workflow/main.py index 7a8d971..bc8a369 100644 --- a/examples/dataset_m5_workflow/main.py +++ b/examples/dataset_m5_workflow/main.py @@ -8,7 +8,6 @@ from shutil import copyfile, rmtree import learnware from learnware.market import instantiate_learnware_market, BaseUserInfo -from learnware.market import database_ops from learnware.reuse import JobSelectorReuser, AveragingReuser from learnware.specification import generate_rkme_spec from m5 import DataLoader @@ -17,24 +16,38 @@ from learnware.logger import get_module_logger logger = get_module_logger("m5_test", level="INFO") +output_description = { + "Dimension": 1, + "Description": {}, +} + +input_description = { + "Dimension": 82, + "Description": {}, +} + semantic_specs = [ { - "Data": {"Values": ["Tabular"], "Type": "Class"}, - "Task": {"Values": ["Classification"], "Type": "Class"}, + "Data": {"Values": ["Table"], "Type": "Class"}, + "Task": {"Values": ["Regression"], "Type": "Class"}, "Library": {"Values": ["Scikit-learn"], "Type": "Class"}, "Scenario": {"Values": ["Business"], "Type": "Tag"}, "Description": {"Values": "", "Type": "String"}, "Name": {"Values": "learnware_1", "Type": "String"}, + "Input": input_description, + "Output": output_description, } ] user_semantic = { - "Data": {"Values": ["Tabular"], "Type": "Class"}, - "Task": {"Values": ["Classification"], "Type": "Class"}, + "Data": {"Values": ["Table"], "Type": "Class"}, + "Task": {"Values": ["Regression"], "Type": "Class"}, "Library": {"Values": ["Scikit-learn"], "Type": "Class"}, "Scenario": {"Values": ["Business"], "Type": "Tag"}, "Description": {"Values": "", "Type": "String"}, "Name": {"Values": "", "Type": "String"}, + "Input": input_description, + "Output": output_description, } @@ -69,8 +82,6 @@ class M5DatasetWorkflow: easy_market.add_learnware(zip_path, semantic_spec) print("Total Item:", len(easy_market)) - curr_inds = easy_market._get_ids() - print("Available ids:", curr_inds) def prepare_learnware(self, regenerate_flag=False): if regenerate_flag: @@ -171,7 +182,7 @@ class M5DatasetWorkflow: job_selector_score = m5.score(test_y, job_selector_predict_y) print(f"mixture reuse loss (job selector): {job_selector_score}") - reuse_ensemble = AveragingReuser(learnware_list=mixture_learnware_list, mode="vote") + reuse_ensemble = AveragingReuser(learnware_list=mixture_learnware_list, mode="vote_by_prob") ensemble_predict_y = reuse_ensemble.predict(user_data=test_x) ensemble_score = m5.score(test_y, ensemble_predict_y) print(f"mixture reuse loss (ensemble): {ensemble_score}\n") diff --git a/examples/dataset_pfs_workflow/example_init.py b/examples/dataset_pfs_workflow/example_init.py index 88b788a..77bad5e 100644 --- a/examples/dataset_pfs_workflow/example_init.py +++ b/examples/dataset_pfs_workflow/example_init.py @@ -6,7 +6,7 @@ from learnware.model import BaseModel class Model(BaseModel): def __init__(self): - super(Model, self).__init__(input_shape=(31,), output_shape=()) + super(Model, self).__init__(input_shape=(31,), output_shape=(1,)) dir_path = os.path.dirname(os.path.abspath(__file__)) self.model = joblib.load(os.path.join(dir_path, "model.out")) diff --git a/examples/dataset_pfs_workflow/main.py b/examples/dataset_pfs_workflow/main.py index d66074e..e0a8fac 100644 --- a/examples/dataset_pfs_workflow/main.py +++ b/examples/dataset_pfs_workflow/main.py @@ -15,25 +15,38 @@ from learnware.logger import get_module_logger logger = get_module_logger("pfs_test", level="INFO") +output_description = { + "Dimension": 1, + "Description": {}, +} + +input_description = { + "Dimension": 31, + "Description": {}, +} semantic_specs = [ { - "Data": {"Values": ["Tabular"], "Type": "Class"}, - "Task": {"Values": ["Classification"], "Type": "Class"}, + "Data": {"Values": ["Table"], "Type": "Class"}, + "Task": {"Values": ["Regression"], "Type": "Class"}, "Library": {"Values": ["Scikit-learn"], "Type": "Class"}, "Scenario": {"Values": ["Business"], "Type": "Tag"}, "Description": {"Values": "", "Type": "String"}, "Name": {"Values": "learnware_1", "Type": "String"}, + "Input": input_description, + "Output": output_description, } ] user_semantic = { - "Data": {"Values": ["Tabular"], "Type": "Class"}, - "Task": {"Values": ["Classification"], "Type": "Class"}, + "Data": {"Values": ["Table"], "Type": "Class"}, + "Task": {"Values": ["Regression"], "Type": "Class"}, "Library": {"Values": ["Scikit-learn"], "Type": "Class"}, "Scenario": {"Values": ["Business"], "Type": "Tag"}, "Description": {"Values": "", "Type": "String"}, "Name": {"Values": "", "Type": "String"}, + "Input": input_description, + "Output": output_description, } @@ -42,7 +55,7 @@ class PFSDatasetWorkflow: pfs = Dataloader() pfs.regenerate_data() - algo_list = ["ridge", "lgb"] + algo_list = ["ridge"] # "ridge", "lgb" for algo in algo_list: pfs.set_algo(algo) pfs.retrain_models() @@ -66,8 +79,6 @@ class PFSDatasetWorkflow: easy_market.add_learnware(zip_path, semantic_spec) print("Total Item:", len(easy_market)) - curr_inds = easy_market._get_ids() - print("Available ids:", curr_inds) def prepare_learnware(self, regenerate_flag=False): if regenerate_flag: @@ -75,7 +86,7 @@ class PFSDatasetWorkflow: pfs = Dataloader() idx_list = pfs.get_idx_list() - algo_list = ["lgb"] # ["ridge", "lgb"] + algo_list = ["ridge"] # ["ridge", "lgb"] curr_root = os.path.dirname(os.path.abspath(__file__)) curr_root = os.path.join(curr_root, "learnware_pool") @@ -157,7 +168,7 @@ class PFSDatasetWorkflow: pred_y = learnware.predict(test_x) loss_list.append(pfs.score(test_y, pred_y)) print( - f"Top1-score: {sorted_score_list[0]}, learnware_id: {single_learnware_list[0].id}, loss: {loss_list[0]}" + f"Top1-score: {sorted_score_list[0]}, learnware_id: {single_learnware_list[0].id}, loss: {loss_list[0]}, random: {np.mean(loss_list)}" ) mixture_id = " ".join([learnware.id for learnware in mixture_learnware_list]) diff --git a/examples/dataset_text_workflow/main.py b/examples/dataset_text_workflow/main.py index bf20091..179bf53 100644 --- a/examples/dataset_text_workflow/main.py +++ b/examples/dataset_text_workflow/main.py @@ -196,10 +196,6 @@ def test_search(gamma=0.1, load_market=True): ensemble_score_list.append(ensemble_score) print(f"mixture reuse accuracy (ensemble): {ensemble_score}") - select_list.append(acc_list[0]) - avg_list.append(np.mean(acc_list)) - improve_list.append((acc_list[0] - np.mean(acc_list)) / np.mean(acc_list)) - # test reuse (ensemblePruning) reuse_pruning = EnsemblePruningReuser(learnware_list=mixture_learnware_list) pruning_predict_y = reuse_pruning.predict(user_data=user_data) diff --git a/examples/dataset_text_workflow2/get_data.py b/examples/dataset_text_workflow2/get_data.py index 216e193..a55e8d7 100644 --- a/examples/dataset_text_workflow2/get_data.py +++ b/examples/dataset_text_workflow2/get_data.py @@ -8,7 +8,9 @@ def get_data(data_root="./data"): dtest = pd.read_csv(os.path.join(data_root, "test.csv")) # returned X(DataFrame), y(Series) - return (dtrain[['discourse_text', 'discourse_type']], - dtrain["discourse_effectiveness"], - dtest[['discourse_text', 'discourse_type']], - dtest["discourse_effectiveness"]) + return ( + dtrain[["discourse_text", "discourse_type"]], + dtrain["discourse_effectiveness"], + dtest[["discourse_text", "discourse_type"]], + dtest["discourse_effectiveness"], + ) diff --git a/examples/dataset_text_workflow2/main.py b/examples/dataset_text_workflow2/main.py index 69765a6..5b3ac96 100644 --- a/examples/dataset_text_workflow2/main.py +++ b/examples/dataset_text_workflow2/main.py @@ -78,10 +78,10 @@ def prepare_model(): modelv_save_path = os.path.join(model_save_root, "uploader_v_%d.pth" % (i)) modell_save_path = os.path.join(model_save_root, "uploader_l_%d.pth" % (i)) - with open(modelv_save_path, 'wb') as f: + with open(modelv_save_path, "wb") as f: pickle.dump(vectorizer, f) - with open(modell_save_path, 'wb') as f: + with open(modell_save_path, "wb") as f: pickle.dump(lgbm, f) logger.info("Model saved to '%s' and '%s'" % (modelv_save_path, modell_save_path)) diff --git a/examples/dataset_text_workflow2/utils.py b/examples/dataset_text_workflow2/utils.py index 4726407..247f706 100644 --- a/examples/dataset_text_workflow2/utils.py +++ b/examples/dataset_text_workflow2/utils.py @@ -39,11 +39,11 @@ def generate_uploader(data_x: pd.Series, data_y: pd.Series, n_uploaders=50, data return os.makedirs(data_save_root, exist_ok=True) - types = data_x['discourse_type'].unique() + types = data_x["discourse_type"].unique() for i in range(n_uploaders): - indices = data_x['discourse_type'] == types[i] - selected_X = data_x[indices]['discourse_text'].to_list() + indices = data_x["discourse_type"] == types[i] + selected_X = data_x[indices]["discourse_text"].to_list() selected_y = data_y[indices].to_list() X_save_dir = os.path.join(data_save_root, "uploader_%d_X.pkl" % (i)) @@ -61,11 +61,11 @@ def generate_user(data_x, data_y, n_users=50, data_save_root=None): return os.makedirs(data_save_root, exist_ok=True) - types = data_x['discourse_type'].unique() + types = data_x["discourse_type"].unique() for i in range(n_users): - indices = data_x['discourse_type'] == types[i] - selected_X = data_x[indices]['discourse_text'].to_list() + indices = data_x["discourse_type"] == types[i] + selected_X = data_x[indices]["discourse_text"].to_list() selected_y = data_y[indices].to_list() X_save_dir = os.path.join(data_save_root, "user_%d_X.pkl" % (i)) @@ -80,10 +80,10 @@ def generate_user(data_x, data_y, n_users=50, data_save_root=None): # Train Uploaders' models def train(X, y, out_classes): - vectorizer = TfidfVectorizer(stop_words='english') + vectorizer = TfidfVectorizer(stop_words="english") X_tfidf = vectorizer.fit_transform(X) - lgbm = LGBMClassifier(boosting_type='dart', n_estimators=500, num_leaves=21) + lgbm = LGBMClassifier(boosting_type="dart", n_estimators=500, num_leaves=21) lgbm.fit(X_tfidf, y) return vectorizer, lgbm diff --git a/learnware/client/container.py b/learnware/client/container.py index 31a2ab1..25ac521 100644 --- a/learnware/client/container.py +++ b/learnware/client/container.py @@ -340,7 +340,6 @@ class ModelDockerContainer(ModelContainer): "install", "-r", f"{requirements_path_filter}", - "--no-dependencies", ] ) ) diff --git a/learnware/client/utils.py b/learnware/client/utils.py index 09e5142..6a15d65 100644 --- a/learnware/client/utils.py +++ b/learnware/client/utils.py @@ -81,7 +81,6 @@ def install_environment(learnware_dirpath, conda_env): "install", "-r", f"{requirements_path_filter}", - "--no-dependencies", ] ) else: diff --git a/learnware/market/easy/checker.py b/learnware/market/easy/checker.py index 5e455a7..6419b98 100644 --- a/learnware/market/easy/checker.py +++ b/learnware/market/easy/checker.py @@ -89,7 +89,6 @@ class EasyStatChecker(BaseChecker): traceback.print_exc() logger.warning(f"The learnware [{learnware.id}] is instantiated failed! Due to {e}.") return self.INVALID_LEARNWARE - try: learnware_model = learnware.get_model() # Check input shape diff --git a/learnware/market/easy/searcher.py b/learnware/market/easy/searcher.py index f86c06c..73bd593 100644 --- a/learnware/market/easy/searcher.py +++ b/learnware/market/easy/searcher.py @@ -1,14 +1,13 @@ import torch import numpy as np from rapidfuzz import fuzz -from cvxopt import solvers, matrix from typing import Tuple, List, Union from .organizer import EasyOrganizer from ..utils import parse_specification_type from ..base import BaseUserInfo, BaseSearcher from ...learnware import Learnware -from ...specification import RKMETableSpecification, RKMEImageSpecification, RKMETextSpecification +from ...specification import RKMETableSpecification, RKMEImageSpecification, RKMETextSpecification, rkme_solve_qp from ...logger import get_module_logger logger = get_module_logger("easy_seacher") @@ -277,19 +276,9 @@ class EasyStatSearcher(BaseSearcher): # weight = torch.linalg.inv(K + torch.eye(K.shape[0]).to(user_rkme.device) * 1e-5) @ C # beta must be nonnegative - n = K.shape[0] - P = matrix(K.cpu().numpy()) - q = matrix(-C.cpu().numpy()) - G = matrix(-np.eye(n)) - h = matrix(np.zeros((n, 1))) - A = matrix(np.ones((1, n))) - b = matrix(np.ones((1, 1))) - solvers.options["show_progress"] = False - sol = solvers.qp(P, q, G, h, A, b) - weight = np.array(sol["x"]) - weight = torch.from_numpy(weight).reshape(-1).double().to(user_rkme.device) - score = user_rkme.inner_prod(user_rkme) + 2 * sol["primal objective"] - + weight, obj = rkme_solve_qp(K, C) + weight = weight.double().to(user_rkme.device) + score = user_rkme.inner_prod(user_rkme) + 2 * obj return weight.detach().cpu().numpy().reshape(-1), score def _calculate_intermediate_K_and_C( @@ -321,7 +310,7 @@ class EasyStatSearcher(BaseSearcher): num = intermediate_K.shape[0] - 1 RKME_list = [learnware.specification.get_stat_spec_by_name(self.stat_spec_type) for learnware in learnware_list] for i in range(intermediate_K.shape[0]): - intermediate_K[num, i] = RKME_list[-1].inner_prod(RKME_list[i]) + intermediate_K[num, i] = intermediate_K[i, num] = RKME_list[-1].inner_prod(RKME_list[i]) intermediate_C[num, 0] = user_rkme.inner_prod(RKME_list[-1]) return intermediate_K, intermediate_C diff --git a/learnware/reuse/job_selector.py b/learnware/reuse/job_selector.py index 5e3a71f..d9f9fbf 100644 --- a/learnware/reuse/job_selector.py +++ b/learnware/reuse/job_selector.py @@ -2,7 +2,7 @@ import torch import numpy as np from typing import List, Union -from cvxopt import matrix, solvers +from qpsolvers import solve_qp from lightgbm import LGBMClassifier, early_stopping from sklearn.metrics import accuracy_score @@ -11,7 +11,7 @@ from .base import BaseReuser from ..market.utils import parse_specification_type from ..learnware import Learnware from ..specification import RKMETableSpecification, RKMETextSpecification -from ..specification import generate_rkme_spec +from ..specification import generate_rkme_spec, rkme_solve_qp from ..logger import get_module_logger logger = get_module_logger("job_selector_reuse") @@ -184,16 +184,8 @@ class JobSelectorReuser(BaseReuser): K = task_rkme_matrix v = np.array([user_rkme_spec.inner_prod(task_rkme) for task_rkme in task_rkme_list]) - P = matrix(K) - q = matrix(-v) - G = matrix(-np.eye(task_num)) - h = matrix(np.zeros((task_num, 1))) - A = matrix(np.ones((1, task_num))) - b = matrix(np.ones((1, 1))) - solvers.options["show_progress"] = False - - sol = solvers.qp(P, q, G, h, A, b, kktsolver="ldl") - task_mixture_weight = np.array(sol["x"]).reshape(-1) + sol, _ = rkme_solve_qp(K, v) + task_mixture_weight = np.array(sol).reshape(-1) return task_mixture_weight diff --git a/learnware/specification/__init__.py b/learnware/specification/__init__.py index 0332890..ce795e9 100644 --- a/learnware/specification/__init__.py +++ b/learnware/specification/__init__.py @@ -5,6 +5,7 @@ from .regular import ( RKMETableSpecification, RKMEImageSpecification, RKMETextSpecification, + rkme_solve_qp, ) from .system import HeteroMapTableSpecification diff --git a/learnware/specification/regular/__init__.py b/learnware/specification/regular/__init__.py index bd01aa9..c9b087a 100644 --- a/learnware/specification/regular/__init__.py +++ b/learnware/specification/regular/__init__.py @@ -2,5 +2,5 @@ from .base import RegularStatSpecification from ...utils import is_torch_avaliable from .text import RKMETextSpecification -from .table import RKMETableSpecification, RKMEStatSpecification +from .table import RKMETableSpecification, RKMEStatSpecification, rkme_solve_qp from .image import RKMEImageSpecification diff --git a/learnware/specification/regular/table/__init__.py b/learnware/specification/regular/table/__init__.py index e8ec903..cc4cda9 100644 --- a/learnware/specification/regular/table/__init__.py +++ b/learnware/specification/regular/table/__init__.py @@ -8,4 +8,4 @@ if not is_torch_avaliable(verbose=False): RKMEStatSpecification = None logger.warning("RKMETableSpecification is skipped because torch is not installed!") else: - from .rkme import RKMETableSpecification, RKMEStatSpecification + from .rkme import RKMETableSpecification, RKMEStatSpecification, rkme_solve_qp diff --git a/learnware/specification/regular/table/rkme.py b/learnware/specification/regular/table/rkme.py index d0f113b..996b9a9 100644 --- a/learnware/specification/regular/table/rkme.py +++ b/learnware/specification/regular/table/rkme.py @@ -1,35 +1,23 @@ from __future__ import annotations import os - import copy import torch import json import codecs import random import numpy as np -from cvxopt import solvers, matrix +from qpsolvers import solve_qp, Problem, solve_problem from collections import Counter from typing import Tuple, Any, List, Union, Dict - -try: - import faiss - - ver = faiss.__version__ - _FAISS_INSTALLED = ver >= "1.7.1" -except ImportError: - _FAISS_INSTALLED = False +import scipy +from sklearn.cluster import MiniBatchKMeans from ..base import RegularStatSpecification from ....logger import get_module_logger logger = get_module_logger("rkme") -if not _FAISS_INSTALLED: - logger.warning( - "Required faiss version >= 1.7.1 is not detected! Please run 'conda install -c pytorch faiss-cpu' first" - ) - class RKMETableSpecification(RegularStatSpecification): """Reduced Kernel Mean Embedding (RKME) Specification""" @@ -125,8 +113,8 @@ class RKMETableSpecification(RegularStatSpecification): self.beta = torch.from_numpy(self.beta).double().to(self.device) return - # Initialize Z by clustering, utiliing faiss to speed up the process. - self._init_z_by_faiss(X, K) + # Initialize Z by clustering, utiliing kmeans to speed up the process. + self._init_z_by_kmeans(X, K) self._update_beta(X, nonnegative_beta) # Alternating optimize Z and beta @@ -137,8 +125,8 @@ class RKMETableSpecification(RegularStatSpecification): # Reshape to original dimensions self.z = self.z.reshape(Z_shape) - def _init_z_by_faiss(self, X: Union[np.ndarray, torch.tensor], K: int): - """Intialize Z by faiss clustering. + def _init_z_by_kmeans(self, X: Union[np.ndarray, torch.tensor], K: int): + """Intialize Z by kmeans clustering. Parameters ---------- @@ -148,10 +136,9 @@ class RKMETableSpecification(RegularStatSpecification): Size of the construced reduced set. """ X = X.astype("float32") - numDim = X.shape[1] - kmeans = faiss.Kmeans(numDim, K, niter=100, verbose=False) - kmeans.train(X) - center = torch.from_numpy(kmeans.centroids).double() + kmeans = MiniBatchKMeans(n_clusters=K, max_iter=100, verbose=False, n_init="auto") + kmeans.fit(X) + center = torch.from_numpy(kmeans.cluster_centers_).double() self.z = center def _update_beta(self, X: Any, nonnegative_beta: bool = True): @@ -177,7 +164,8 @@ class RKMETableSpecification(RegularStatSpecification): C = torch.sum(C, dim=1) / X.shape[0] if nonnegative_beta: - beta = solve_qp(K, C).to(self.device) + beta, _ = rkme_solve_qp(K, C) + beta = beta.to(self.device) else: beta = torch.linalg.inv(K + torch.eye(K.shape[0]).to(self.device) * 1e-5) @ C @@ -536,7 +524,7 @@ def torch_rbf_kernel(x1, x2, gamma) -> torch.Tensor: return torch.exp(-X12norm * gamma) -def solve_qp(K: np.ndarray, C: np.ndarray): +def rkme_solve_qp(K: np.ndarray, C: np.ndarray): """Solver for the following quadratic programming(QP) problem: - min 1/2 x^T K x - C^T x s.t 1^T x - 1 = 0 @@ -554,18 +542,24 @@ def solve_qp(K: np.ndarray, C: np.ndarray): torch.tensor Solution to the QP problem. """ + if torch.is_tensor(K): + K = K.cpu().numpy() + if torch.is_tensor(C): + C = C.cpu().numpy() + C = C.reshape(-1) + n = K.shape[0] - P = matrix(K.cpu().numpy()) - q = matrix(-C.cpu().numpy()) - G = matrix(-np.eye(n)) - h = matrix(np.zeros((n, 1))) - A = matrix(np.ones((1, n))) - b = matrix(np.ones((1, 1))) - - solvers.options["show_progress"] = False - sol = solvers.qp(P, q, G, h, A, b) # Requires the sum of x to be 1 - # sol = solvers.qp(P, q, G, h) # Otherwise - w = np.array(sol["x"]) + P = np.array(K) + P = scipy.sparse.csc_matrix(P) + q = np.array(-C) + G = np.array(-np.eye(n)) + G = scipy.sparse.csc_matrix(G) + h = np.array(np.zeros((n, 1))) + A = np.array(np.ones((1, n))) + A = scipy.sparse.csc_matrix(A) + b = np.array(np.ones((1, 1))) + problem = Problem(P, q, G, h, A, b) + solution = solve_problem(problem, solver="clarabel") + w = solution.x w = torch.from_numpy(w).reshape(-1) - - return w + return w, solution.obj diff --git a/setup.py b/setup.py index 8f2f812..2c0df29 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,6 @@ REQUIRED = [ "numpy>=1.20.0", "pandas>=0.25.1", "scipy>=1.0.0", - "cvxopt>=1.3.0", "tqdm>=4.65.0", "scikit-learn>=0.22", "joblib>=1.2.0", @@ -68,11 +67,9 @@ REQUIRED = [ "langdetect>=1.0.9", "huggingface-hub<0.18", "portalocker>=2.0.0", + "qpsolvers[clarabel]>=4.0.1", ] -if get_platform() != MACOS: - REQUIRED.append("faiss-cpu>=1.7.1") - here = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(here, "README.md"), encoding="utf-8") as f: long_description = f.read()