]> granicus.if.org Git - liblinear/commitdiff
Enable installation with PyPI
authorkevin1kevin1k <kevin1kevin1k@gmail.com>
Wed, 10 Feb 2021 10:03:03 +0000 (18:03 +0800)
committerkevin1kevin1k <kevin1kevin1k@gmail.com>
Wed, 10 Feb 2021 10:03:03 +0000 (18:03 +0800)
Co-authored-by: Yu-Sheng Li <kevin1kevin1k@gmail.com>
Co-authored-by: Jui-Nan Yen <gkevinyen5418@gmail.com>
python/MANIFEST.in [new file with mode: 0644]
python/README
python/liblinear/__init__.py [new file with mode: 0644]
python/liblinear/commonutil.py [moved from python/commonutil.py with 100% similarity]
python/liblinear/liblinear.py [moved from python/liblinear.py with 95% similarity]
python/liblinear/liblinearutil.py [moved from python/liblinearutil.py with 98% similarity]
python/setup.py [new file with mode: 0644]

diff --git a/python/MANIFEST.in b/python/MANIFEST.in
new file mode 100644 (file)
index 0000000..0746184
--- /dev/null
@@ -0,0 +1,2 @@
+include  cpp-source/*
+include  cpp-source/*/*
index 6449974b8059553082f795bb66a6a1be68cbf95b..349d8a755170e4ca0c7b0e5de370460d2e8e1387 100644 (file)
@@ -26,6 +26,12 @@ interface is developed with the built-in Python library "ctypes."
 Installation
 ============
 
+To install via pip or conda, run
+
+> pip install -U liblinear
+
+Alternatively, you may also directly use the Python interface without installing this package; see the following.
+
 On Unix systems, type
 
 > make
@@ -49,7 +55,7 @@ functions in liblinearutil.py and commonutil.py (shared with LIBSVM
 and imported by svmutil.py). The usage is the same as the LIBLINEAR
 MATLAB interface.
 
->>> from liblinearutil import *
+>>> from liblinear.liblinearutil import *
 # Read data in LIBSVM format
 >>> y, x = svm_read_problem('../heart_scale')
 >>> m = train(y[:200], x[:200], '-c 4')
@@ -77,7 +83,7 @@ The low-level use directly calls C interfaces imported by liblinear.py. Note tha
 all arguments and return values are in ctypes format. You need to handle them
 carefully.
 
->>> from liblinear import *
+>>> from liblinear.liblinear import *
 >>> prob = problem([1,-1], [{1:1, 3:1}, {1:-1,3:-1}])
 >>> param = parameter('-c 4')
 >>> m = liblinear.train(prob, param) # m is a ctype pointer to a model
@@ -95,7 +101,7 @@ There are two levels of usage. The high-level one uses utility functions
 in liblinearutil.py and the usage is the same as the LIBLINEAR MATLAB interface.
 
 >>> import scipy
->>> from liblinearutil import *
+>>> from liblinear.liblinearutil import *
 # Read data in LIBSVM format
 >>> y, x = svm_read_problem('../heart_scale', return_scipy = True) # y: ndarray, x: csr_matrix
 >>> m = train(y[:200], x[:200, :], '-c 4')
@@ -128,7 +134,7 @@ The low-level use directly calls C interfaces imported by liblinear.py. Note tha
 all arguments and return values are in ctypes format. You need to handle them
 carefully.
 
->>> from liblinear import *
+>>> from liblinear.liblinear import *
 >>> prob = problem(scipy.asarray([1,-1]), scipy.sparse.csr_matrix(([1, 1, -1, -1], ([0, 0, 1, 1], [0, 2, 0, 2]))))
 >>> param = parameter('-c 4')
 >>> m = liblinear.train(prob, param) # m is a ctype pointer to a model
@@ -163,7 +169,7 @@ fields and methods.
 Before using the data structures, execute the following command to load the
 LIBLINEAR shared library:
 
-    >>> from liblinear import *
+    >>> from liblinear.liblinear import *
 
 - class feature_node:
 
@@ -304,7 +310,7 @@ Utility Functions
 
 To use utility functions, type
 
-    >>> from liblinearutil import *
+    >>> from liblinear.liblinearutil import *
 
 The above command loads
     train()            : train a linear model
@@ -458,7 +464,7 @@ The above command loads
 Additional Information
 ======================
 
-This interface was written by Hsiang-Fu Yu from Department of Computer
+This interface was originally written by Hsiang-Fu Yu from Department of Computer
 Science, National Taiwan University. If you find this tool useful, please
 cite LIBLINEAR as follows
 
diff --git a/python/liblinear/__init__.py b/python/liblinear/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
similarity index 95%
rename from python/liblinear.py
rename to python/liblinear/liblinear.py
index 64e21bb8c2d52ee5a3d1a1b363b9d15eedde13cc..c22312981e3c0a86bee2f2e17e29c14de4384e71 100644 (file)
@@ -3,6 +3,7 @@
 from ctypes import *
 from ctypes.util import find_library
 from os import path
+from glob import glob
 import sys
 
 try:
@@ -25,18 +26,23 @@ __all__ = ['liblinear', 'feature_node', 'gen_feature_nodearray', 'problem',
 
 try:
        dirname = path.dirname(path.abspath(__file__))
-       if sys.platform == 'win32':
-               liblinear = CDLL(path.join(dirname, r'..\windows\liblinear.dll'))
-       else:
-               liblinear = CDLL(path.join(dirname, '../liblinear.so.4'))
+       dynamic_lib_name = 'clib.cp*'
+       path_to_so = glob(path.join(dirname, dynamic_lib_name))[0]
+       liblinear = CDLL(path_to_so)
 except:
-# For unix the prefix 'lib' is not considered.
-       if find_library('linear'):
-               liblinear = CDLL(find_library('linear'))
-       elif find_library('liblinear'):
-               liblinear = CDLL(find_library('liblinear'))
-       else:
-               raise Exception('LIBLINEAR library not found.')
+       try :
+               if sys.platform == 'win32':
+                       liblinear = CDLL(path.join(dirname, r'..\..\windows\liblinear.dll'))
+               else:
+                       liblinear = CDLL(path.join(dirname, '../../liblinear.so.4'))
+       except:
+       # For unix the prefix 'lib' is not considered.
+               if find_library('linear'):
+                       liblinear = CDLL(find_library('linear'))
+               elif find_library('liblinear'):
+                       liblinear = CDLL(find_library('liblinear'))
+               else:
+                       raise Exception('LIBLINEAR library not found.')
 
 L2R_LR = 0
 L2R_L2LOSS_SVC_DUAL = 1
similarity index 98%
rename from python/liblinearutil.py
rename to python/liblinear/liblinearutil.py
index ce24bcfb9a21a5d4cc590125b7e8ffa3b0957799..073ca4152b2f8fdb081b9856986216f40565ff1e 100644 (file)
@@ -2,11 +2,11 @@
 
 import os, sys
 sys.path = [os.path.dirname(os.path.abspath(__file__))] + sys.path
-from liblinear import *
-from liblinear import __all__ as liblinear_all
-from liblinear import scipy, sparse
-from commonutil import *
-from commonutil import __all__ as common_all
+from .liblinear import *
+from .liblinear import __all__ as liblinear_all
+from .liblinear import scipy, sparse
+from .commonutil import *
+from .commonutil import __all__ as common_all
 from ctypes import c_double
 
 if sys.version_info[0] < 3:
diff --git a/python/setup.py b/python/setup.py
new file mode 100644 (file)
index 0000000..7483925
--- /dev/null
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+import sys, os
+from os import path
+from shutil import copyfile, rmtree
+from glob import glob
+
+from setuptools import setup, Extension
+from distutils.command.clean import clean as clean_cmd
+
+# a technique to build a shared library on windows
+from distutils.command.build_ext import build_ext
+
+build_ext.get_export_symbols = lambda x, y: []
+
+
+PACKAGE_NAME = "liblinear-official"
+VERSION = "2.42.0"
+cpp_dir = "cpp-source"
+# should be consistent with dynamic_lib_name in liblinear/liblinear.py
+dynamic_lib_name = "clib"
+
+# sources to be included to build the shared library
+source_codes = [
+    path.join("blas", "daxpy.c"),
+    path.join("blas", "ddot.c"),
+    path.join("blas", "dnrm2.c"),
+    path.join("blas", "dscal.c"),
+    "linear.cpp",
+    "newton.cpp",
+]
+headers = [
+    path.join("blas", "blas.h"),
+    path.join("blas", "blasp.h"),
+    "newton.h",
+    "linear.h",
+    "linear.def",
+]
+
+kwargs_for_extension = {
+    "sources": [path.join(cpp_dir, f) for f in source_codes],
+    "depends": [path.join(cpp_dir, f) for f in headers],
+    "include_dirs": [cpp_dir],
+    "language": "c++",
+}
+
+# see ../Makefile.win
+if sys.platform == "win32":
+    kwargs_for_extension.update(
+        {
+            "define_macros": [("_WIN64", ""), ("_CRT_SECURE_NO_DEPRECATE", "")],
+            "extra_link_args": ["-DEF:{}\linear.def".format(cpp_dir)],
+        }
+    )
+
+
+def create_cpp_source():
+    for f in source_codes + headers:
+        src_file = path.join("..", f)
+        tgt_file = path.join(cpp_dir, f)
+        # ensure blas directory is created
+        os.makedirs(path.dirname(tgt_file), exist_ok=True)
+        copyfile(src_file, tgt_file)
+
+
+class CleanCommand(clean_cmd):
+    def run(self):
+        clean_cmd.run(self)
+        to_be_removed = ["build/", "dist/", "MANIFEST", cpp_dir, "{}.egg-info".format(PACKAGE_NAME)]
+        to_be_removed += glob("./{}/{}.*".format(PACKAGE_NAME, dynamic_lib_name))
+        for root, dirs, files in os.walk(os.curdir, topdown=False):
+            if "__pycache__" in dirs:
+                to_be_removed.append(path.join(root, "__pycache__"))
+            to_be_removed += [f for f in files if f.endswith(".pyc")]
+
+        for f in to_be_removed:
+            print("remove {}".format(f))
+            if f == ".":
+                continue
+            elif path.isfile(f):
+                os.remove(f)
+            elif path.isdir(f):
+                rmtree(f)
+
+def main():
+    if not path.exists(cpp_dir):
+        create_cpp_source()
+
+    with open("README") as f:
+        long_description = f.read()
+
+    setup(
+        name=PACKAGE_NAME,
+        packages=[PACKAGE_NAME],
+        version=VERSION,
+        description="Python binding of LIBLINEAR",
+        long_description=long_description,
+        long_description_content_type="text/plain",
+        author="ML group @ National Taiwan University",
+        author_email="cjlin@csie.ntu.edu.tw",
+        url="https://www.csie.ntu.edu.tw/~cjlin/liblinear",
+        install_requires=["scipy"],
+        ext_modules=[
+            Extension(
+                "{}.{}".format(PACKAGE_NAME, dynamic_lib_name), **kwargs_for_extension
+            )
+        ],
+        cmdclass={"clean": CleanCommand},
+    )
+
+
+main()
+