From 6f22e66cc068a0cdf6d3af99b77e84e28785f89b Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 30 Nov 2022 11:56:30 +0800
Subject: [PATCH 1/6] [Add] Auto_Run

---
 Auto_Run.py    | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
 algo_para.yaml |  8 +++++++
 2 files changed, 72 insertions(+)
 create mode 100644 Auto_Run.py
 create mode 100644 algo_para.yaml

diff --git a/Auto_Run.py b/Auto_Run.py
new file mode 100644
index 0000000..b6ff117
--- /dev/null
+++ b/Auto_Run.py
@@ -0,0 +1,64 @@
+import pandas as pd
+import numpy as np
+import yaml
+from sklearn.neighbors import KNeighborsClassifier
+from sklearn.model_selection import train_test_split
+from FS.pso import jfs as jfs_pso
+
+
+class DataPipeline(object):
+    """
+    Data Model with Pipeline to Process and Feature Select.
+    """
+    def __init__(self, _ori_data_path:str, _train_test_ratio:float, _str_read_meta_para:str) -> None:
+        """
+        para1:_ori_data_path:Read_RawData_Path(CSV_Format) => Convert to DataFrame 
+        para2:_train_test_ratio:Split DataSet Ratio
+        """
+        self.data = None
+        self.feat = None
+        self.label = None
+
+        self.fold = None # Contain Splited Train/Test Data
+        self.meta_para = None
+
+        self._load_ori_file(_ori_data_path)
+        self._data_split(_train_test_ratio)
+        self._read_algo_parameter(_str_read_meta_para)
+        self._process_feature_select()
+
+    def _load_ori_file(self, _ori_path) -> None:
+        self.data = pd.read_csv(_ori_path)
+        self.data  = self.data.values
+        #All Feature
+        self.feat  = np.asarray(self.data[:, 0:-1]) 
+        #Predict Y Value
+        self.label = np.asarray(self.data[:, -1])
+        #print(self.data)
+
+    def _data_split(self, ratio:float) -> None:
+        xtrain, xtest, ytrain, ytest = train_test_split(self.feat, self.label, test_size=ratio, stratify=self.label)
+        self.fold = {'xt':xtrain, 'yt':ytrain, 'xv':xtest, 'yv':ytest}
+
+    def _read_algo_parameter(self, _str_read_meta_para) ->None:
+        with open('algo_para.yaml', 'r') as _str_read_meta_para:
+            self.meta_para = yaml.full_load(_str_read_meta_para)['meta_para']
+            print(self.meta_para)
+    
+    def _process_feature_select(self) -> None:
+        # perform feature selection
+        ## Append Fold Data Into OPTS~~~~~~~~~~~~~~~ 
+        fmdl = jfs_pso(self.feat, self.label, self.meta_para[0]['opts'])
+        sf   = fmdl['sf']
+
+def main():
+    str_read_file_path = './ionosphere.csv'
+    str_read_meta_para = './algo_para.yaml'
+    float_split_ratio = 0.3
+
+    auto_run = DataPipeline(str_read_file_path, float_split_ratio, str_read_meta_para)
+
+
+if __name__ == '__main__':
+    main()
+    
\ No newline at end of file
diff --git a/algo_para.yaml b/algo_para.yaml
new file mode 100644
index 0000000..f452a18
--- /dev/null
+++ b/algo_para.yaml
@@ -0,0 +1,8 @@
+meta_para:
+- name: pso
+  # k-value in KNN
+  # number of particles
+  # maximum number of iterations
+  opts: {'k': 5, 'N': 10, 'T': 100} 
+- name: ga
+  opts: 300
\ No newline at end of file

From 891da3735d27a9d377a3b0f7a2aa2f09aa788cf1 Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 30 Nov 2022 16:01:50 +0800
Subject: [PATCH 2/6] Auto_Run_By_Yaml_Setting

Add Each Feature Select Model and Running by One Python File
---
 Auto_Run.py    | 92 +++++++++++++++++++++++++++++++++++++++++++-------
 algo_para.yaml | 47 ++++++++++++++++++++++----
 2 files changed, 121 insertions(+), 18 deletions(-)

diff --git a/Auto_Run.py b/Auto_Run.py
index b6ff117..58775ef 100644
--- a/Auto_Run.py
+++ b/Auto_Run.py
@@ -3,7 +3,17 @@
 import yaml
 from sklearn.neighbors import KNeighborsClassifier
 from sklearn.model_selection import train_test_split
+import matplotlib.pyplot as plt
+
 from FS.pso import jfs as jfs_pso
+from FS.ga import jfs as jfs_ga
+from FS.de import jfs as jfs_de
+from FS.ba import jfs as jfs_ba
+from FS.cs import jfs as jfs_cs
+from FS.fa import jfs as jfs_fa
+from FS.fpa import jfs as jfs_fpa
+from FS.sca import jfs as jfs_sca
+from FS.woa import jfs as jfs_woa
 
 
 class DataPipeline(object):
@@ -15,6 +25,10 @@ def __init__(self, _ori_data_path:str, _train_test_ratio:float, _str_read_meta_p
         para1:_ori_data_path:Read_RawData_Path(CSV_Format) => Convert to DataFrame 
         para2:_train_test_ratio:Split DataSet Ratio
         """
+        self._ori_data_path = _ori_data_path
+        self._train_test_ratio = _train_test_ratio
+        self._str_read_meta_para = _str_read_meta_para
+
         self.data = None
         self.feat = None
         self.label = None
@@ -22,10 +36,8 @@ def __init__(self, _ori_data_path:str, _train_test_ratio:float, _str_read_meta_p
         self.fold = None # Contain Splited Train/Test Data
         self.meta_para = None
 
-        self._load_ori_file(_ori_data_path)
-        self._data_split(_train_test_ratio)
-        self._read_algo_parameter(_str_read_meta_para)
-        self._process_feature_select()
+        self.sf = None # Select_Feature 
+        self.fmdl = None # Feature Model
 
     def _load_ori_file(self, _ori_path) -> None:
         self.data = pd.read_csv(_ori_path)
@@ -34,22 +46,78 @@ def _load_ori_file(self, _ori_path) -> None:
         self.feat  = np.asarray(self.data[:, 0:-1]) 
         #Predict Y Value
         self.label = np.asarray(self.data[:, -1])
-        #print(self.data)
 
     def _data_split(self, ratio:float) -> None:
         xtrain, xtest, ytrain, ytest = train_test_split(self.feat, self.label, test_size=ratio, stratify=self.label)
         self.fold = {'xt':xtrain, 'yt':ytrain, 'xv':xtest, 'yv':ytest}
 
-    def _read_algo_parameter(self, _str_read_meta_para) ->None:
+    def _read_algo_parameter(self, _str_read_meta_para:str) ->None:
         with open('algo_para.yaml', 'r') as _str_read_meta_para:
             self.meta_para = yaml.full_load(_str_read_meta_para)['meta_para']
-            print(self.meta_para)
     
-    def _process_feature_select(self) -> None:
+    def _build_fmdl(self, algo_name):
+        interface_fmdl = {'pso': jfs_pso,'ga':jfs_ga, 'de':jfs_de, 'ba':jfs_ba, 'cs':jfs_cs, 'fa': jfs_fa, 'fpa':jfs_fpa, 'sca':jfs_sca, 'woa':jfs_woa}
+        return interface_fmdl[algo_name]   
+
+    def _process_feature_select(self, strMetaName:str, listMetaOpts:list) -> None:
         # perform feature selection
-        ## Append Fold Data Into OPTS~~~~~~~~~~~~~~~ 
-        fmdl = jfs_pso(self.feat, self.label, self.meta_para[0]['opts'])
-        sf   = fmdl['sf']
+        ## Append Fold Data Into OPTS
+        #print(self._build_fmdl('ga'))
+        listMetaOpts['fold'] = self.fold
+        #self.fmdl = jfs_ga(self.feat, self.label, listMetaOpts)
+        self.fmdl = self._build_fmdl(strMetaName)(self.feat, self.label, listMetaOpts)
+        self.sf   = self.fmdl['sf']
+    
+    def _data_feature_select(self, strMetaName:str, listMetaOpts:list)->None:
+        # model with selected features
+        num_train = np.size(self.fold['xt'], 0)
+        num_valid = np.size(self.fold['xv'], 0)
+        x_train   = self.fold['xt'][:, self.sf]
+        y_train   = self.fold['yt'].reshape(num_train)  # Solve bug
+        x_valid   = self.fold['xv'][:, self.sf]
+        y_valid   = self.fold['yv'].reshape(num_valid)  # Solve bug
+
+        mdl       = KNeighborsClassifier(n_neighbors = listMetaOpts['k']) 
+        mdl.fit(x_train, y_train)
+
+        # accuracy
+        y_pred    = mdl.predict(x_valid)
+        Acc       = np.sum(y_valid == y_pred)  / num_valid
+        print("Accuracy:", 100 * Acc)
+
+        # number of selected features
+        num_feat = self.fmdl['nf']
+        print("Feature Size:", num_feat)
+
+    def _plot_converege(self, strMetaName:str, listMetaOpts:list)-> None:
+        '''
+        plot convergence
+        '''
+        curve   = self.fmdl['c']
+        curve   = curve.reshape(np.size(curve,1))
+        x       = np.arange(0, listMetaOpts['T'], 1.0) + 1.0
+        fig, ax = plt.subplots()
+        ax.plot(x, curve, 'o-')
+        ax.set_xlabel('Number of Iterations')
+        ax.set_ylabel('Fitness')
+        ax.set_title(strMetaName)
+        ax.grid()
+        plt.show()
+    
+    def proceed(self)->None:
+        '''
+        Execute Function
+        '''
+        self._load_ori_file(self._ori_data_path)
+        self._data_split(self._train_test_ratio)
+        self._read_algo_parameter(self._str_read_meta_para)
+
+        for meta in self.meta_para:
+            print(meta['name'])
+            print(meta['opts'])
+            self._process_feature_select(meta['name'], meta['opts'])
+            self._data_feature_select(meta['name'], meta['opts'])
+            self._plot_converege(meta['name'], meta['opts'])
 
 def main():
     str_read_file_path = './ionosphere.csv'
@@ -57,7 +125,7 @@ def main():
     float_split_ratio = 0.3
 
     auto_run = DataPipeline(str_read_file_path, float_split_ratio, str_read_meta_para)
-
+    auto_run.proceed()
 
 if __name__ == '__main__':
     main()
diff --git a/algo_para.yaml b/algo_para.yaml
index f452a18..271157b 100644
--- a/algo_para.yaml
+++ b/algo_para.yaml
@@ -1,8 +1,43 @@
-meta_para:
+meta_para: ## Ref: https://github.com/tongysmember/Wrapper-Feature-Selection-Toolbox-Python/blob/main/Description.md#differential-evolution-de
 - name: pso
-  # k-value in KNN
-  # number of particles
-  # maximum number of iterations
-  opts: {'k': 5, 'N': 10, 'T': 100} 
+  # K :  k-value in KNN
+  # N :  number of particles
+  # T :  maximum number of iterations
+  opts: {'k': 5, 'N': 10, 'T': 10} 
 - name: ga
-  opts: 300
\ No newline at end of file
+  # K :  k-value in KNN
+  # N :  number of particles
+  # T :  maximum number of iterations
+  # T
+  # MR
+  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.8, 'MR': 0.01}
+- name: de
+  # CR : crossover rate
+  # F  : constant factor
+  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.9, 'F': 0.5}
+- name: ba
+  #fmax   = 2      # maximum frequency
+  #fmin   = 0      # minimum frequency
+  #alpha  = 0.9    # constant
+  #gamma  = 0.9    # constant
+  #A      = 2      # maximum loudness
+  #r      = 1      # maximum pulse rate
+  opts: {'k': 5, 'N': 10, 'T': 10, 'fmax': 2, 'fmin': 0, 'alpha': 0.9, 'gamma': 0.9, 'A': 2, 'r': 1}
+- name: cs
+  #Pa  = 0.25   # discovery rate
+  opts: {'k': 5, 'N': 10, 'T': 10, 'Pa': 0.25}
+- name: fa
+  #alpha  = 1       # constant
+  #beta0  = 1       # light amplitude
+  #gamma  = 1       # absorbtion coefficient
+  #theta  = 0.97    # control alpha
+  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 1, 'beta0': 1, 'gamma': 1, 'theta': 0.97}
+- name: fpa
+  #P  = 0.8      # switch probability
+  opts: {'k': 5, 'N': 10, 'T': 10, 'P': 0.8}
+- name: sca
+  #alpha  = 2    # constant
+  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 2} 
+- name: woa
+  #b  = 1    # constant
+  opts: {'k': 5, 'N': 10, 'T': 10, 'b': 1}
\ No newline at end of file

From e0197079b98e73dfc7b4ca2cd862dd10fd84d2ab Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 30 Nov 2022 16:09:06 +0800
Subject: [PATCH 3/6] Add Comment

[Update]Add Comment
---
 Auto_Run.py | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/Auto_Run.py b/Auto_Run.py
index 58775ef..f86d14d 100644
--- a/Auto_Run.py
+++ b/Auto_Run.py
@@ -40,6 +40,9 @@ def __init__(self, _ori_data_path:str, _train_test_ratio:float, _str_read_meta_p
         self.fmdl = None # Feature Model
 
     def _load_ori_file(self, _ori_path) -> None:
+        '''
+        Load Raw Data File By CSV Path
+        '''
         self.data = pd.read_csv(_ori_path)
         self.data  = self.data.values
         #All Feature
@@ -48,28 +51,41 @@ def _load_ori_file(self, _ori_path) -> None:
         self.label = np.asarray(self.data[:, -1])
 
     def _data_split(self, ratio:float) -> None:
+        '''
+        Setting Train/Test Ratio
+        '''
         xtrain, xtest, ytrain, ytest = train_test_split(self.feat, self.label, test_size=ratio, stratify=self.label)
         self.fold = {'xt':xtrain, 'yt':ytrain, 'xv':xtest, 'yv':ytest}
 
     def _read_algo_parameter(self, _str_read_meta_para:str) ->None:
+        '''
+        Read Meta Para File
+        '''
         with open('algo_para.yaml', 'r') as _str_read_meta_para:
             self.meta_para = yaml.full_load(_str_read_meta_para)['meta_para']
     
     def _build_fmdl(self, algo_name):
+        '''
+        Construct Each Meta Model 
+        '''
         interface_fmdl = {'pso': jfs_pso,'ga':jfs_ga, 'de':jfs_de, 'ba':jfs_ba, 'cs':jfs_cs, 'fa': jfs_fa, 'fpa':jfs_fpa, 'sca':jfs_sca, 'woa':jfs_woa}
         return interface_fmdl[algo_name]   
 
     def _process_feature_select(self, strMetaName:str, listMetaOpts:list) -> None:
+        """
+        Prepare MetaModel and X Feature Data
+        """
         # perform feature selection
         ## Append Fold Data Into OPTS
-        #print(self._build_fmdl('ga'))
         listMetaOpts['fold'] = self.fold
         #self.fmdl = jfs_ga(self.feat, self.label, listMetaOpts)
         self.fmdl = self._build_fmdl(strMetaName)(self.feat, self.label, listMetaOpts)
         self.sf   = self.fmdl['sf']
     
     def _data_feature_select(self, strMetaName:str, listMetaOpts:list)->None:
-        # model with selected features
+        """
+         model with selected features
+        """
         num_train = np.size(self.fold['xt'], 0)
         num_valid = np.size(self.fold['xv'], 0)
         x_train   = self.fold['xt'][:, self.sf]

From 0b79657b6a559b0084ad3948d8130e52fcbbf2d6 Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 30 Nov 2022 16:14:45 +0800
Subject: [PATCH 4/6] Update ReadME.md

add Auto_Run_Desc
---
 README.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index b5c363a..a8df18d 100644
--- a/README.md
+++ b/README.md
@@ -215,4 +215,7 @@ plt.show()
 | 01  | `ga`         | [Genetic Algorithm](/Description.md#genetic-algorithm-ga)                                   | -    | Yes              |
 
 
-
+## Auto run 
+* You can run multiple-model in one python code(Auto_Run.py), compare with model result.
+* Model List: pso, ga, de, ba, cs, fa, fpa, sca, woa
+* Setting File: algo_para.yaml

From 1d7c6b0710a64d89f3d22e75a841c922788ac2c2 Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 7 Dec 2022 22:38:19 +0800
Subject: [PATCH 5/6] [Update] UpperBound_LowerBound / Abstract Function

---
 FS/__basic.py  | 32 ++++++++++++++++++++++++++++++++
 FS/ba.py       | 37 ++++---------------------------------
 FS/cs.py       | 35 +++--------------------------------
 FS/de.py       | 35 +++--------------------------------
 FS/fa.py       | 35 +++--------------------------------
 FS/fpa.py      | 34 ++++------------------------------
 FS/ga.py       | 27 +++------------------------
 FS/gwo.py      | 35 +++--------------------------------
 FS/hho.py      | 35 +++--------------------------------
 FS/ja.py       | 34 +++-------------------------------
 FS/pso.py      | 35 +++--------------------------------
 FS/sca.py      | 35 +++--------------------------------
 FS/ssa.py      | 34 +++-------------------------------
 FS/woa.py      | 36 +++---------------------------------
 algo_para.yaml | 18 +++++++++---------
 15 files changed, 82 insertions(+), 415 deletions(-)
 create mode 100644 FS/__basic.py

diff --git a/FS/__basic.py b/FS/__basic.py
new file mode 100644
index 0000000..7b9c712
--- /dev/null
+++ b/FS/__basic.py
@@ -0,0 +1,32 @@
+import numpy as np
+from numpy.random import rand
+
+
+def init_position(lb, ub, N, dim):
+    X = np.zeros([N, dim], dtype='float')
+    for i in range(N):
+        for d in range(dim):
+            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
+    
+    return X
+
+
+def binary_conversion(X, thres, N, dim):
+    Xbin = np.zeros([N, dim], dtype='int')
+    for i in range(N):
+        for d in range(dim):
+            if X[i,d] > thres:
+                Xbin[i,d] = 1
+            else:
+                Xbin[i,d] = 0
+    
+    return Xbin
+
+
+def boundary(x, lb, ub):
+    if x < lb:
+        x = lb
+    if x > ub:
+        x = ub
+    
+    return x
\ No newline at end of file
diff --git a/FS/ba.py b/FS/ba.py
index 1ab3e80..41e3c96 100644
--- a/FS/ba.py
+++ b/FS/ba.py
@@ -3,42 +3,13 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-    
+from FS.__basic import init_position, binary_conversion, boundary
+ 
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub     = 1
-    lb     = 0
+    ub     = opts['ub']
+    lb     = opts['lb']
     thres  = 0.5
     fmax   = 2      # maximum frequency
     fmin   = 0      # minimum frequency
diff --git a/FS/cs.py b/FS/cs.py
index da6998d..1001bd6 100644
--- a/FS/cs.py
+++ b/FS/cs.py
@@ -3,39 +3,10 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
+from FS.__basic import init_position, binary_conversion, boundary
 import math
 
 
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-
-
 # Levy Flight
 def levy_distribution(beta, dim):
     # Sigma     
@@ -54,8 +25,8 @@ def levy_distribution(beta, dim):
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub     = 1
-    lb     = 0
+    ub     = opts['ub']
+    lb     = opts['lb']
     thres  = 0.5
     Pa     = 0.25     # discovery rate
     alpha  = 1        # constant
diff --git a/FS/de.py b/FS/de.py
index 9903258..77019b2 100644
--- a/FS/de.py
+++ b/FS/de.py
@@ -3,42 +3,13 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
+from FS.__basic import init_position, binary_conversion, boundary
     
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     CR    = 0.9     # crossover rate
     F     = 0.5     # factor
diff --git a/FS/fa.py b/FS/fa.py
index 85cb0bf..792c390 100644
--- a/FS/fa.py
+++ b/FS/fa.py
@@ -3,42 +3,13 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
+from FS.__basic import init_position, binary_conversion, boundary
 
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub     = 1
-    lb     = 0
+    ub     = opts['ub']
+    lb     = opts['lb']
     thres  = 0.5
     alpha  = 1       # constant
     beta0  = 1       # light amplitude
diff --git a/FS/fpa.py b/FS/fpa.py
index f21a6ec..7627c60 100644
--- a/FS/fpa.py
+++ b/FS/fpa.py
@@ -3,37 +3,11 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-import math
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
+from FS.__basic import init_position, binary_conversion, boundary
 
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
+import math
 
 
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
     
 
 # Levy Flight
@@ -54,8 +28,8 @@ def levy_distribution(beta, dim):
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub     = 1
-    lb     = 0
+    ub     = opts['ub']
+    lb     = opts['lb']
     thres  = 0.5
     beta   = 1.5    # levy component
     P      = 0.8    # switch probability
diff --git a/FS/ga.py b/FS/ga.py
index ed48729..2ba48a9 100644
--- a/FS/ga.py
+++ b/FS/ga.py
@@ -1,28 +1,7 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
+from FS.__basic import init_position, binary_conversion, boundary
 
 def roulette_wheel(prob):
     num = len(prob)
@@ -38,8 +17,8 @@ def roulette_wheel(prob):
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub       = 1
-    lb       = 0
+    ub       = opts['ub']
+    lb       = opts['lb']
     thres    = 0.5    
     CR       = 0.8     # crossover rate
     MR       = 0.01    # mutation rate
diff --git a/FS/gwo.py b/FS/gwo.py
index 0bdb35c..7244fb3 100644
--- a/FS/gwo.py
+++ b/FS/gwo.py
@@ -3,42 +3,13 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
+from FS.__basic import init_position, binary_conversion, boundary
 
 
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-    
-
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     
     N        = opts['N']
diff --git a/FS/hho.py b/FS/hho.py
index e54c64c..ec49e4f 100644
--- a/FS/hho.py
+++ b/FS/hho.py
@@ -3,39 +3,10 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
+from FS.__basic import init_position, binary_conversion, boundary
 import math
 
 
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-
-
 def levy_distribution(beta, dim):
     # Sigma 
     nume  = math.gamma(1 + beta) * np.sin(np.pi * beta / 2)
@@ -53,8 +24,8 @@ def levy_distribution(beta, dim):
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     beta  = 1.5    # levy component
     
diff --git a/FS/ja.py b/FS/ja.py
index 3592a10..48bdd1f 100644
--- a/FS/ja.py
+++ b/FS/ja.py
@@ -3,42 +3,14 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
+from FS.__basic import init_position, binary_conversion, boundary
 
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
     
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub     = 1
-    lb     = 0
+    ub     = opts['ub']
+    lb     = opts['lb']
     thres  = 0.5
     
     N          = opts['N']
diff --git a/FS/pso.py b/FS/pso.py
index 495e11d..7491c9d 100644
--- a/FS/pso.py
+++ b/FS/pso.py
@@ -1,15 +1,7 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
+from FS.__basic import init_position, binary_conversion, boundary
 
 
 def init_velocity(lb, ub, N, dim):
@@ -26,33 +18,12 @@ def init_velocity(lb, ub, N, dim):
             V[i,d] = Vmin[0,d] + (Vmax[0,d] - Vmin[0,d]) * rand()
         
     return V, Vmax, Vmin
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
     
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     w     = 0.9    # inertia weight
     c1    = 2      # acceleration factor
diff --git a/FS/sca.py b/FS/sca.py
index 9e1616e..6c3418e 100644
--- a/FS/sca.py
+++ b/FS/sca.py
@@ -3,42 +3,13 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
+from FS.__basic import init_position, binary_conversion, boundary
 
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     alpha = 2       # constant
     
diff --git a/FS/ssa.py b/FS/ssa.py
index 53f8cce..df5841c 100644
--- a/FS/ssa.py
+++ b/FS/ssa.py
@@ -3,42 +3,14 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
+from FS.__basic import init_position, binary_conversion, boundary
 
 
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     
     N        = opts['N']
diff --git a/FS/woa.py b/FS/woa.py
index a6f7a8f..403324e 100644
--- a/FS/woa.py
+++ b/FS/woa.py
@@ -3,42 +3,12 @@
 import numpy as np
 from numpy.random import rand
 from FS.functionHO import Fun
-
-
-def init_position(lb, ub, N, dim):
-    X = np.zeros([N, dim], dtype='float')
-    for i in range(N):
-        for d in range(dim):
-            X[i,d] = lb[0,d] + (ub[0,d] - lb[0,d]) * rand()        
-    
-    return X
-
-
-def binary_conversion(X, thres, N, dim):
-    Xbin = np.zeros([N, dim], dtype='int')
-    for i in range(N):
-        for d in range(dim):
-            if X[i,d] > thres:
-                Xbin[i,d] = 1
-            else:
-                Xbin[i,d] = 0
-    
-    return Xbin
-
-
-def boundary(x, lb, ub):
-    if x < lb:
-        x = lb
-    if x > ub:
-        x = ub
-    
-    return x
-
+from FS.__basic import init_position, binary_conversion, boundary
 
 def jfs(xtrain, ytrain, opts):
     # Parameters
-    ub    = 1
-    lb    = 0
+    ub    = opts['ub']
+    lb    = opts['lb']
     thres = 0.5
     b     = 1       # constant
     
diff --git a/algo_para.yaml b/algo_para.yaml
index 271157b..dfe7c12 100644
--- a/algo_para.yaml
+++ b/algo_para.yaml
@@ -3,18 +3,18 @@ meta_para: ## Ref: https://github.com/tongysmember/Wrapper-Feature-Selection-Too
   # K :  k-value in KNN
   # N :  number of particles
   # T :  maximum number of iterations
-  opts: {'k': 5, 'N': 10, 'T': 10} 
+  opts: {'k': 5, 'N': 10, 'T': 10, 'ub': 1, 'lb': 0} 
 - name: ga
   # K :  k-value in KNN
   # N :  number of particles
   # T :  maximum number of iterations
   # T
   # MR
-  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.8, 'MR': 0.01}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.8, 'MR': 0.01, 'ub': 1, 'lb': 0}
 - name: de
   # CR : crossover rate
   # F  : constant factor
-  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.9, 'F': 0.5}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'CR': 0.9, 'F': 0.5, 'ub': 1, 'lb': 0}
 - name: ba
   #fmax   = 2      # maximum frequency
   #fmin   = 0      # minimum frequency
@@ -22,22 +22,22 @@ meta_para: ## Ref: https://github.com/tongysmember/Wrapper-Feature-Selection-Too
   #gamma  = 0.9    # constant
   #A      = 2      # maximum loudness
   #r      = 1      # maximum pulse rate
-  opts: {'k': 5, 'N': 10, 'T': 10, 'fmax': 2, 'fmin': 0, 'alpha': 0.9, 'gamma': 0.9, 'A': 2, 'r': 1}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'fmax': 2, 'fmin': 0, 'alpha': 0.9, 'gamma': 0.9, 'A': 2, 'r': 1, 'ub': 1, 'lb': 0}
 - name: cs
   #Pa  = 0.25   # discovery rate
-  opts: {'k': 5, 'N': 10, 'T': 10, 'Pa': 0.25}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'Pa': 0.25, 'ub': 1, 'lb': 0}
 - name: fa
   #alpha  = 1       # constant
   #beta0  = 1       # light amplitude
   #gamma  = 1       # absorbtion coefficient
   #theta  = 0.97    # control alpha
-  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 1, 'beta0': 1, 'gamma': 1, 'theta': 0.97}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 1, 'beta0': 1, 'gamma': 1, 'theta': 0.97, 'ub': 1, 'lb': 0}
 - name: fpa
   #P  = 0.8      # switch probability
-  opts: {'k': 5, 'N': 10, 'T': 10, 'P': 0.8}
+  opts: {'k': 5, 'N': 10, 'T': 10, 'P': 0.8, 'ub': 1, 'lb': 0}
 - name: sca
   #alpha  = 2    # constant
-  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 2} 
+  opts: {'k': 5, 'N': 10, 'T': 10, 'alpha': 2, 'ub': 1, 'lb': 0} 
 - name: woa
   #b  = 1    # constant
-  opts: {'k': 5, 'N': 10, 'T': 10, 'b': 1}
\ No newline at end of file
+  opts: {'k': 5, 'N': 10, 'T': 10, 'b': 1, 'ub': 1, 'lb': 0}
\ No newline at end of file

From 0ba0d8cd41f67a3d8c69eb022ffdade5fde8ff81 Mon Sep 17 00:00:00 2001
From: Mike Tung <tongysmember@gmail.com>
Date: Wed, 7 Dec 2022 22:53:46 +0800
Subject: [PATCH 6/6] [ADD] Transform_Function

---
 FS/__basic.py     | 15 ++++++++-------
 FS/__tran_func.py | 10 ++++++++++
 2 files changed, 18 insertions(+), 7 deletions(-)
 create mode 100644 FS/__tran_func.py

diff --git a/FS/__basic.py b/FS/__basic.py
index 7b9c712..8512751 100644
--- a/FS/__basic.py
+++ b/FS/__basic.py
@@ -1,6 +1,6 @@
 import numpy as np
-from numpy.random import rand
-
+from numpy.random import rand 
+from FS.__tran_func import tran_func
 
 def init_position(lb, ub, N, dim):
     X = np.zeros([N, dim], dtype='float')
@@ -10,12 +10,12 @@ def init_position(lb, ub, N, dim):
     
     return X
 
-
-def binary_conversion(X, thres, N, dim):
+def binary_conversion(X, thres, N, dim, trans_func=tran_func.sl_trans):
     Xbin = np.zeros([N, dim], dtype='int')
     for i in range(N):
         for d in range(dim):
-            if X[i,d] > thres:
+            X_trans = trans_func(X[i,d])
+            if X_trans > thres:
                 Xbin[i,d] = 1
             else:
                 Xbin[i,d] = 0
@@ -28,5 +28,6 @@ def boundary(x, lb, ub):
         x = lb
     if x > ub:
         x = ub
-    
-    return x
\ No newline at end of file
+
+    return x
+
diff --git a/FS/__tran_func.py b/FS/__tran_func.py
new file mode 100644
index 0000000..af88323
--- /dev/null
+++ b/FS/__tran_func.py
@@ -0,0 +1,10 @@
+import numpy as np
+class tran_func():
+    
+    @staticmethod
+    def l_trans(val):
+        return val
+
+    @staticmethod
+    def sl_trans(val):
+        return 1 / (1+np.exp(-2 * val))    
\ No newline at end of file