diff --git a/docs/ZOOpt/Parameters-in-ZOOpt.rst b/docs/ZOOpt/Parameters-in-ZOOpt.rst
index 00f49ff..c09fa34 100644
--- a/docs/ZOOpt/Parameters-in-ZOOpt.rst
+++ b/docs/ZOOpt/Parameters-in-ZOOpt.rst
@@ -137,6 +137,9 @@ Parameter
    class that implements the   member function ``check(self, optcontent)``, which will be invoked at each iteration of the optimization. The optimization algorithm will  stop in advance if ``stopping_criterion.check()`` returns True.
 -  ``seed`` sets the seed of all generated random numbers used in ZOOpt.
 -  ``parallel`` and ``server_num`` are set for parallel optimization.
+-  ``shrinking_rate`` is the region shrinking rate of ``RACE-CARS``.
+-  ``shrinking_freq`` is the region shrinking freqency of ``RACE-CARS``.
+-  ``max_shrinking_times`` is the maximum shrinking times of ``RACE-CARS``.
 
 
 
@@ -275,3 +278,15 @@ the variance of the projection matrix A. By default, ``withdraw_alpha``
 equals to ``Dimension(1, [[-1, 1]], [True])`` and ``variance_A`` equals
 to ``1/d`` (``d`` is the dimension size of the ``low_dimension``). In
 most cases, it's not necessary for users to provide them.
+the population set.
+
+Optimize a function leveraging randomized region shrinking
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+ZOOpt implements an acceleration for sequential ``RACOS`` utilizing randomized region shrinking (``RACE-CARS``).
+
+.. code:: python
+
+    parameter = Parameter(budget=3000, shrinking_rate=0.9, shrinking_freq=0.01)
+
+In ZOOpt, the ``RACE-CARS`` acceleration is automatically enabled when both ``shrinking_rate`` (controls how aggressively the search space shrinks) and ``shrinking_freq`` (determines how often shrinking occurs) parameters are specified. Additionally, ``max_shrinking_times`` can be configured to cap the total number of space reductions during optimization.
\ No newline at end of file
diff --git a/zoopt/__init__.py b/zoopt/__init__.py
index cb34c30..c11c361 100644
--- a/zoopt/__init__.py
+++ b/zoopt/__init__.py
@@ -1,6 +1,6 @@
-from zoopt.dimension import Dimension, Dimension2, ValueType
-from zoopt.objective import Objective
-from zoopt.opt import Opt
-from zoopt.parameter import Parameter
-from zoopt.solution import Solution
-from zoopt.exp_opt import ExpOpt
+from .dimension import Dimension, Dimension2, ValueType
+from .objective import Objective
+from .opt import Opt
+from .parameter import Parameter
+from .solution import Solution
+from .exp_opt import ExpOpt
diff --git a/zoopt/algos/opt_algorithms/racos/racecars.py b/zoopt/algos/opt_algorithms/racos/racecars.py
new file mode 100644
index 0000000..62fe5c1
--- /dev/null
+++ b/zoopt/algos/opt_algorithms/racos/racecars.py
@@ -0,0 +1,233 @@
+"""
+This module contains the class SRacos, which is the sequential version of Racos (a classification based optimization algorithm).
+
+Author:
+    Yu-ren Liu
+
+Updated by:
+    Ze-Wen Li
+"""
+
+import time
+
+import numpy as np
+from copy import deepcopy
+
+from zoopt.algos.opt_algorithms.racos.racos_classification import RacosClassification
+from zoopt.algos.opt_algorithms.racos.racos_common import RacosCommon
+from zoopt.utils.tool_function import ToolFunction
+from zoopt import Objective, Solution, Parameter
+
+
+class RaceCars(RacosCommon):
+    """
+    The class SRacos represents Sequential Racos algorithm. It's inherited from RacosCommon.
+    """
+
+    def __init__(self):
+        """
+        Initialization.
+        """
+        RacosCommon.__init__(self)
+        return
+
+    def opt(self, objective: Objective, parameter: Parameter, strategy='WR', ub=1):
+        """
+        SRacos optimization.
+
+        :param objective: an Objective object
+        :param parameter: a Parameter object
+        :param strategy: replace strategy
+        :param ub: uncertain bits, which is a parameter of SRacos
+        :return: Optimization result
+        """
+        self.clear()
+        self.set_objective(objective)
+        self.set_parameters(parameter)
+        self.init_attribute()
+        stopping_criterion = self._parameter.get_stopping_criterion()
+        i = 0
+        k = 1
+        iteration_num = self._parameter.get_budget() - self._parameter.get_train_size()
+        time_log1 = time.time()
+        max_distinct_repeat_times = 100
+        current_not_distinct_times = 0
+        shrinking_rate = self._parameter.get_shrinking_rate()
+        shrinking_freq = self._parameter.get_shrinking_freq()
+        max_shrinking_times = self._parameter.get_max_shrinking_times()
+        dim = deepcopy(self._objective.get_dim())
+        while i < iteration_num:
+            sampled_data = self._positive_data + self._negative_data
+            if max_shrinking_times is not None:
+                if k <= max_shrinking_times:
+                    if np.random.rand() <= shrinking_freq:
+                        self._objective.shrink_dim(self._best_solution.get_x(), dim, shrinking_rate, k)
+                        k += 1
+            else:
+                if np.random.rand() <= shrinking_freq:
+                    self._objective.shrink_dim(self._best_solution.get_x(), dim, shrinking_rate, k)
+                    k += 1
+            if np.random.rand() < self._parameter.get_probability():
+                classifier = RacosClassification(
+                    self._objective.get_dim(), self._positive_data, self._negative_data, ub)
+                classifier.mixed_classification()
+                solution, distinct_flag = self.distinct_sample_classifier(
+                    classifier, sampled_data, True, self._parameter.get_train_size())
+            else:
+                solution, distinct_flag = self.distinct_sample(dim, sampled_data)
+            # panic stop
+            if solution is None:
+                ToolFunction.log(" [break loop] because solution is None")
+                return self._best_solution
+            if distinct_flag is False:
+                current_not_distinct_times += 1
+                if current_not_distinct_times >= max_distinct_repeat_times:
+                    ToolFunction.log(
+                        "[break loop] because distinct_flag is false too much times")
+                    return self._best_solution
+                else:
+                    continue
+            # evaluate the solution
+            objective.eval(solution)
+            # show best solution
+            times = i + self._parameter.get_train_size() + 1
+            self.show_best_solution(parameter.get_intermediate_result(), times, parameter.get_intermediate_freq())
+            bad_ele = self.replace(self._positive_data, solution, 'pos')
+            self.replace(self._negative_data, bad_ele, 'neg', strategy)
+            self._best_solution = self._positive_data[0]
+            if i == 4:
+                time_log2 = time.time()
+                expected_time = (self._parameter.get_budget() - self._parameter.get_train_size()) * \
+                                (time_log2 - time_log1) / 5
+                if self._parameter.get_time_budget() is not None:
+                    expected_time = min(
+                        expected_time, self._parameter.get_time_budget())
+                if expected_time > 5:
+                    m, s = divmod(expected_time, 60)
+                    h, m = divmod(m, 60)
+                    ToolFunction.log(
+                        'expected remaining running time: %02d:%02d:%02d' % (h, m, s))
+            # time budget check
+            if self._parameter.get_time_budget() is not None:
+                if (time.time() - time_log1) >= self._parameter.get_time_budget():
+                    ToolFunction.log('time_budget runs out')
+                    return self._best_solution
+            # terminal_value check
+            if self._parameter.get_terminal_value() is not None:
+                if self._best_solution.get_value() <= self._parameter.get_terminal_value():
+                    ToolFunction.log('terminal function value reached')
+                    return self._best_solution
+            if stopping_criterion.check(self) is True:
+                return self._best_solution
+            i += 1
+        return self._best_solution
+
+    def replace(self, iset, x, iset_type, strategy='WR'):
+        """
+        Replace a solution(chosen by strategy) in iset with x.
+
+        :param iset: a solution list
+        :param x: a Solution object
+        :param iset_type: 'pos' or 'neg'
+        :param strategy: 'WR': worst replace or 'RR': random replace or 'LM': replace the farthest solution
+        :return: the replaced solution
+        """
+        if strategy == 'WR':
+            return self.strategy_wr(iset, x, iset_type)
+        elif strategy == 'RR':
+            return self.strategy_rr(iset, x)
+        elif strategy == 'LM':
+            best_sol = min(iset, key=lambda x: x.get_value())
+            return self.strategy_lm(iset, best_sol, x)
+
+    def binary_search(self, iset, x, begin, end):
+        """
+        Find the first element larger than x.
+
+        :param iset: a solution set
+        :param x: a Solution object
+        :param begin: begin position
+        :param end: end position
+        :return: the index of the first element larger than x
+        """
+        x_value = x.get_value()
+        if x_value <= iset[begin].get_value():
+            return begin
+        if x_value >= iset[end].get_value():
+            return end + 1
+        if end == begin + 1:
+            return end
+        mid = begin + (end - begin) // 2
+        if x_value <= iset[mid].get_value():
+            return self.binary_search(iset, x, begin, mid)
+        else:
+            return self.binary_search(iset, x, mid, end)
+
+    def strategy_wr(self, iset, x, iset_type):
+        """
+        Replace the worst solution in iset.
+
+        :param iset: a solution set
+        :param x: a Solution object
+        :param iset_type: 'pos' or 'neg'
+        :return: the worst solution
+        """
+        if iset_type == 'pos':
+            index = self.binary_search(iset, x, 0, len(iset) - 1)
+            iset.insert(index, x)
+            worst_ele = iset.pop()
+        else:
+            worst_ele, worst_index = Solution.find_maximum(iset)
+            if worst_ele.get_value() > x.get_value():
+                iset[worst_index] = x
+            else:
+                worst_ele = x
+        return worst_ele
+
+    def strategy_rr(self, iset, x):
+        """
+        Replace a random solution in iset.
+
+        :param iset: a solution set
+        :param x: a Solution object
+        :return: the replaced solution
+        """
+        len_iset = len(iset)
+        replace_index = np.random.randint(0, len_iset)
+        replace_ele = iset[replace_index]
+        iset[replace_index] = x
+        return replace_ele
+
+    #
+    def strategy_lm(self, iset, best_sol, x):
+        """
+        Replace the farthest solution from best_sol
+
+        :param iset: a solution set
+        :param best_sol: the best solution, distance between solution in iset and best_sol will be computed
+        :param x: a Solution object
+        :return: the farthest solution (has the largest margin) in iset
+        """
+        farthest_dis = 0
+        farthest_index = 0
+        for i in range(len(iset)):
+            dis = self.distance(iset[i].get_x(), best_sol.get_x())
+            if dis > farthest_dis:
+                farthest_dis = dis
+                farthest_index = i
+        farthest_ele = iset[farthest_index]
+        iset[farthest_index] = x
+        return farthest_ele
+
+    @staticmethod
+    def distance(x, y):
+        """
+        Get the distance between the list x and y
+        :param x: a list
+        :param y: a list
+        :return: Euclidean distance
+        """
+        dis = 0
+        for i in range(len(x)):
+            dis += (x[i] - y[i])**2
+        return np.sqrt(dis)
\ No newline at end of file
diff --git a/zoopt/algos/opt_algorithms/racos/racos_classification.py b/zoopt/algos/opt_algorithms/racos/racos_classification.py
index 0ab521b..92e2477 100644
--- a/zoopt/algos/opt_algorithms/racos/racos_classification.py
+++ b/zoopt/algos/opt_algorithms/racos/racos_classification.py
@@ -8,12 +8,17 @@
     Ze-Wen Li
 """
 
-from zoopt.dimension import Dimension, Dimension2
+from zoopt.dimension import Dimension, Dimension2, ValueType
 from zoopt.utils.tool_function import ToolFunction
 import copy
 import numpy as np
 from zoopt.utils.zoo_global import gl
 
+def sol_in_regs(sol, regs):
+    for i in range(len(sol)):
+        if sol[i] < regs[i][0] or sol[i] > regs[i][1]:
+            return False
+    return True
 
 class RacosClassification:
     """
@@ -42,7 +47,6 @@ def __init__(self, dim, positive, negative, ub=1):
         for i in range(dim.get_size()):
             temp = [regions[i][0], regions[i][1]]
             self.__sample_region.append(temp)
-        return
 
     def reset_classifier(self):
         """
@@ -66,8 +70,11 @@ def mixed_classification(self):
         :return: no return value
         """
         if type(self.__solution_space) == Dimension:
-            self.__x_positive = self.__positive_solution[np.random.randint(
+            while True:
+                self.__x_positive = self.__positive_solution[np.random.randint(
                 0, len(self.__positive_solution))]
+                if sol_in_regs(self.__x_positive.get_x(), self.__sample_region):
+                    break
             len_negative = len(self.__negative_solution)
             index_set = list(range(self.__solution_space.get_size()))
             remain_index_set = list(range(self.__solution_space.get_size()))
@@ -76,7 +83,7 @@ def mixed_classification(self):
             while len_negative > 0:
                 if len(remain_index_set) == 0:
                     ToolFunction.log('ERROR: sampled two same solutions, please raise issues on github')
-                k = remain_index_set[np.random.randint(0, len(remain_index_set))]
+                k = remain_index_set[np.random.randint(0, len(remain_index_set))]  # randomly choose a index k
                 x_pos_k = self.__x_positive.get_x_index(k)
                 # continuous
                 if types[k] is True:
@@ -85,7 +92,7 @@ def mixed_classification(self):
                     x_neg_k = x_negative.get_x_index(k)
                     if x_pos_k < x_neg_k:
                         r = np.random.uniform(x_pos_k, x_neg_k)
-                        if r < self.__sample_region[k][1]:
+                        if r <= self.__sample_region[k][1]:
                             self.__sample_region[k][1] = r
                             i = 0
                             while i < len_negative:
@@ -98,7 +105,7 @@ def mixed_classification(self):
                                     i += 1
                     else:
                         r = np.random.uniform(x_neg_k, x_pos_k)
-                        if r > self.__sample_region[k][0]:
+                        if r >= self.__sample_region[k][0]:
                             self.__sample_region[k][0] = r
                             i = 0
                             while i < len_negative:
@@ -118,7 +125,7 @@ def mixed_classification(self):
                         if x_pos_k < x_neg_k:
                             # different from continuous version
                             r = np.random.randint(x_pos_k, x_neg_k)
-                            if r < self.__sample_region[k][1]:
+                            if r <= self.__sample_region[k][1]:
                                 self.__sample_region[k][1] = r
                                 i = 0
                                 while i < len_negative:
@@ -131,7 +138,7 @@ def mixed_classification(self):
                                         i += 1
                         else:
                             r = np.random.randint(x_neg_k, x_pos_k + 1)
-                            if r > self.__sample_region[k][0]:
+                            if r >= self.__sample_region[k][0]:
                                 self.__sample_region[k][0] = r
                                 i = 0
                                 while i < len_negative:
@@ -162,20 +169,23 @@ def mixed_classification(self):
             self.set_uncertain_bit(index_set)
             return
         elif type(self.__solution_space) == Dimension2:
-            self.__x_positive = self.__positive_solution[np.random.randint(0, len(self.__positive_solution))]
-            len_negative = len(self.__negative_solution)
+            while True:
+                self.__x_positive = self.__positive_solution[np.random.randint(
+                0, len(self.__positive_solution))]
+                if sol_in_regs(self.__x_positive.get_x(), self.__sample_region):
+                    break
             index_set = list(range(self.__solution_space.get_size()))
             remain_index_set = list(range(self.__solution_space.get_size()))
-            types = self.__solution_space.get_types()
+            types = self.__solution_space.get_types()  # continuous, discrete, grid
             order_or_precision = self.__solution_space.get_order_or_precision()
             while len_negative > 0:
                 if len(remain_index_set) == 0:
                     ToolFunction.log('ERROR: sampled two same solutions, please raise issues on github')
-                k = remain_index_set[np.random.randint(0, len(remain_index_set))]
+                k = remain_index_set[np.random.randint(0, len(remain_index_set))]  # randomly choose a index k
                 x_pos_k = self.__x_positive.get_x_index(k)
 
                 # continuous
-                if types[k]:
+                if types[k] == ValueType.CONTINUOUS:
                     x_negative = self.__negative_solution[np.random.randint(0, len_negative)]
                     x_neg_k = x_negative.get_x_index(k)
 
@@ -205,7 +215,7 @@ def mixed_classification(self):
                                     self.__negative_solution[len_negative] = itemp
                                 else:
                                     i += 1
-                # discrete
+                # discrete and grid
                 else:
                     if order_or_precision[k] is True:
                         x_negative = self.__negative_solution[np.random.randint(0, len_negative)]
@@ -236,7 +246,7 @@ def mixed_classification(self):
                                         self.__negative_solution[len_negative] = itemp
                                     else:
                                         i += 1
-                    else:
+                    else: # for unordered discrete or grid valuetype
                         delete = 0
                         i = 0
                         while i < len_negative:
@@ -285,9 +295,12 @@ def rand_sample(self):
             x = copy.deepcopy(self.__x_positive.get_x())
             for index in self.__label_index:
                 # continuous
-                if self.__solution_space.get_type(index):
+                if self.__solution_space.get_type(index) == ValueType.CONTINUOUS:
                     x[index] = round(np.random.uniform(self.__sample_region[index][0], self.__sample_region[index][1]),
                                      gl.float_precisions[index])
+                # grid
+                elif self.__solution_space.get_type(index) == ValueType.GRID:
+                    x[index] = np.random.choice(self.__sample_region[index], 1)[0]
                 # discrete
                 else:
                     x[index] = np.random.randint(self.__sample_region[index][0], self.__sample_region[index][1] + 1)
diff --git a/zoopt/algos/opt_algorithms/racos/racos_common.py b/zoopt/algos/opt_algorithms/racos/racos_common.py
old mode 100755
new mode 100644
index 024b25f..961f8d6
--- a/zoopt/algos/opt_algorithms/racos/racos_common.py
+++ b/zoopt/algos/opt_algorithms/racos/racos_common.py
@@ -27,6 +27,9 @@ def __init__(self):
         # Solution set
         # Random sampled solutions construct self._data
         self._data = []
+        # Save solutions with distinct x for tune init
+        self._init_data = []
+        self._need_copy = True
         # self._positive_data are best-positive_size solutions set
         self._positive_data = []
         # self._negative_data are the other solutions
@@ -152,8 +155,28 @@ def tune_init_attribute(self):
         :return: sample x
         """
         self._parameter.set_negative_size(self._parameter.get_train_size() - self._parameter.get_positive_size())
-        x, distinct_flag = self.distinct_sample(self._objective.get_dim(), self._data, data_num=1)
-        return x
+        if self._need_copy:
+            self._data_temp = copy.deepcopy(self._parameter.get_init_samples())
+            self._need_copy = False
+            self._iteration_num = self._parameter.get_train_size()
+        if self._data_temp is not None and self._best_solution is None:
+            size = min(len(self._data_temp), self._iteration_num)
+            if size > 0:
+                if isinstance(self._data_temp[0], Solution) is False:
+                    x = self._objective.construct_solution(self._data_temp[0])
+                else:
+                    x = self._data_temp[0]
+                del self._data_temp[0]
+                self._iteration_num -= 1
+                self._init_data.append(x)
+                if math.isnan(x.get_value()):
+                    return x, True
+                else:
+                    return self.tune_init_attribute()
+        x, distinct_flag = self.distinct_sample(self._objective.get_dim(), self._init_data, data_num=1)
+        if distinct_flag:
+            self._init_data.append(x)
+        return x, distinct_flag
 
     def selection(self):
         """
@@ -167,10 +190,9 @@ def selection(self):
         """
 
         new_data = sorted(self._data, key=lambda x: x.get_value())
-        self._data = new_data[0:self._parameter.get_train_size()]
-        self._positive_data = new_data[0: self._parameter.get_positive_size()]
-        self._negative_data = new_data[
-            self._parameter.get_positive_size(): self._parameter.get_train_size()]
+        self._data = new_data[0: self._parameter.get_train_size()]
+        self._positive_data = self._data[0: self._parameter.get_positive_size()]
+        self._negative_data = self._data[self._parameter.get_positive_size():]
         self._best_solution = self._positive_data[0]
         return
 
diff --git a/zoopt/algos/opt_algorithms/racos/racos_optimization.py b/zoopt/algos/opt_algorithms/racos/racos_optimization.py
index a56aa09..c09eeed 100644
--- a/zoopt/algos/opt_algorithms/racos/racos_optimization.py
+++ b/zoopt/algos/opt_algorithms/racos/racos_optimization.py
@@ -8,6 +8,7 @@
 from zoopt.algos.noise_handling.ssracos import SSRacos
 from zoopt.algos.opt_algorithms.racos.racos import Racos
 from zoopt.algos.opt_algorithms.racos.sracos import SRacos
+from zoopt.algos.opt_algorithms.racos.racecars import RaceCars
 from zoopt.algos.opt_algorithms.racos.asracos import ASRacos
 
 
@@ -52,6 +53,8 @@ def opt(self, objective, parameter, strategy='WR'):
             if parameter.get_sequential():
                 if parameter.get_noise_handling() is True and parameter.get_suppression() is True:
                     self.__algorithm = SSRacos()
+                elif parameter.get_region_shrinking():
+                    self.__algorithm = RaceCars()
                 else:
                     self.__algorithm = SRacos()
                 self.__best_solution = self.__algorithm.opt(
diff --git a/zoopt/algos/opt_algorithms/racos/sracos.py b/zoopt/algos/opt_algorithms/racos/sracos.py
index 93f606f..71c45af 100644
--- a/zoopt/algos/opt_algorithms/racos/sracos.py
+++ b/zoopt/algos/opt_algorithms/racos/sracos.py
@@ -143,7 +143,7 @@ def binary_search(self, iset, x, begin, end):
             return end + 1
         if end == begin + 1:
             return end
-        mid = (begin + end) // 2
+        mid = begin + (end - begin) // 2
         if x_value <= iset[mid].get_value():
             return self.binary_search(iset, x, begin, mid)
         else:
@@ -224,7 +224,7 @@ class SRacosTune(RacosCommon):
     The class SRacosTune represents Sequential Racos algorithm for Tune. It's inherited from RacosCommon.
     """
 
-    def __init__(self, dimension, parameter):
+    def __init__(self, dimension, parameter, **kwargs):
         """
         Initialization.
 
@@ -237,10 +237,12 @@ def __init__(self, dimension, parameter):
         objective = Objective(None, dimension)
         self.set_objective(objective)
         self.set_parameters(parameter)
+        self._parameter.set_server_num(kwargs['parallel_num'])
 
         self.init_num = 0
         self.complete_num = 0
         self.semaphore = 1  # control init
+        self.live_num = 0
         self.ub = self._parameter.get_uncertain_bits()
         if self.ub is None:
             self.ub = self.choose_ub(self.get_objective())
@@ -254,14 +256,22 @@ def suggest(self):
         if self.semaphore == 0:
             return
 
+        solution = None
+
         if self.init_num < self._parameter.get_train_size():  # for init
-            solution = self.tune_init_attribute()
+            solution, distinct_flag = self.tune_init_attribute()
+            if distinct_flag is False:
+                return "FINISHED"
+            self.live_num += 1
         elif self.init_num == self._parameter.get_train_size():
             self.semaphore = 0
             self.init_num += 1
             return
-        else:
-            solution = self.sample_solution(self.ub)
+        elif self.live_num < self._parameter.get_server_num():
+            solution, distinct_flag = self.sample_solution(self.ub)
+            if distinct_flag is False:
+                return "FINISHED"
+            self.live_num += 1
 
         self.init_num += 1
         return solution
@@ -275,16 +285,16 @@ def complete(self, solution, result):
         :return: best solution so far
         """
         self.complete_num += 1
+        self.live_num -= 1
+        solution.set_value(result)
         if self.complete_num < self._parameter.get_train_size():
-            solution.set_value(result)
-            self._data.append(solution)
-        elif self.complete_num == self._parameter.get_train_size():
-            solution.set_value(result)
             self._data.append(solution)
             self.selection()
+        elif self.complete_num == self._parameter.get_train_size():
+            best_solution_so_far = self.update_classifier(solution)
             self.semaphore = 1
+            return best_solution_so_far
         else:
-            solution.set_value(result)
             best_solution_so_far = self.update_classifier(solution)
             return best_solution_so_far
 
@@ -296,18 +306,16 @@ def sample_solution(self, ub=1):
         :return: a solution containing trial
         """
         sampled_data = self._positive_data + self._negative_data
-        if np.random.random() < self._parameter.get_probability():
+        if np.random.random() < self._parameter.get_probability():  # exploitation
             classifier = RacosClassification(
                 self._objective.get_dim(), self._positive_data, self._negative_data, ub)
             classifier.mixed_classification()
             solution, distinct_flag = self.distinct_sample_classifier(
                 classifier, sampled_data, True, self._parameter.get_train_size())
-        else:
+        else:  # exploration
             solution, distinct_flag = self.distinct_sample(self._objective.get_dim(), sampled_data)
 
-        sampled_data.append(solution)
-
-        return solution
+        return solution, distinct_flag
 
     def update_classifier(self, solution, strategy='WR'):
         stopping_criterion = self._parameter.get_stopping_criterion()
@@ -360,7 +368,7 @@ def binary_search(self, iset, x, begin, end):
             return end + 1
         if end == begin + 1:
             return end
-        mid = (begin + end) // 2
+        mid = begin + (end - begin) // 2
         if x_value <= iset[mid].get_value():
             return self.binary_search(iset, x, begin, mid)
         else:
diff --git a/zoopt/dimension.py b/zoopt/dimension.py
index b613558..22c2765 100644
--- a/zoopt/dimension.py
+++ b/zoopt/dimension.py
@@ -8,8 +8,8 @@
     Ze-Wen Li
 """
 
-from zoopt.utils.zoo_global import gl
-from zoopt.utils.tool_function import ToolFunction
+from .utils.zoo_global import gl
+from .utils.tool_function import ToolFunction
 import numpy as np
 import copy
 
@@ -91,7 +91,7 @@ def set_dimension_size(self, size):
         self._size = size
         return
 
-    def set_region(self, index, reg, ty):
+    def set_region(self, index, reg, ty=True):
         if index > self._size - 1:
             ToolFunction.log('dimension.py: index out of bound')
             return
@@ -99,7 +99,7 @@ def set_region(self, index, reg, ty):
         self._types[index] = ty
         return
 
-    def set_regions(self, regs, tys):
+    def set_regions(self, regs, tys=True):
         if self.judge_match(self._size, regs, tys) is False:
             return
         self._regions = regs
@@ -219,6 +219,7 @@ def print_dim(self):
 
 
 class ValueType(enumerate):
+    GRID = 2
     CONTINUOUS = 1
     DISCRETE = 0
 
@@ -238,14 +239,16 @@ def __init__(self, dim_list=[]):
                                         e.g.: (ValueType.CONTINUOUS, [0, 1], 1e-6)
                     for discrete dimension: (type, range, has_partial_order)
                                         e.g.: (ValueType.DISCRETE, [0, 1], False)
+                    for grid dimension: (type, values)
+                                        e.g.: (ValueType.GRID, ['first value', 'second value'])
 
         """
         gl.float_precisions = []
         self._size = len(dim_list)
         self._regions = list(map(lambda x: x[1], dim_list))
-        # True means continuous, False means discrete
-        self._types = list(map(lambda x: x[0] == True, dim_list))
-        self._order_or_precision = list(map(lambda x: x[2], dim_list))
+        self._types = list(map(lambda x: x[0], dim_list))
+        self._order_or_precision = list(map(lambda x: x[2] if len(x) == 3 else None,
+                                            dim_list))  # Note: for grid valuetype, len(x)=2
 
         for _dim in dim_list:
             if _dim[0] == ValueType.CONTINUOUS:
@@ -347,7 +350,9 @@ def rand_sample(self):
         """
         x = []
         for i in range(self._size):
-            if self._types[i]:
+            if self._types[i] == ValueType.GRID:
+                value = np.random.choice(self._regions[i], 1)[0]
+            elif self._types[i] == ValueType.CONTINUOUS:
                 value = round(np.random.uniform(self._regions[i][0], self._regions[i][1]), gl.float_precisions[i])
             else:
                 value = np.random.randint(self._regions[i][0], self._regions[i][1] + 1)
@@ -364,8 +369,10 @@ def limited_space(self):
         """
         number = 1
         for i in range(self._size):
-            if self._types[i]:
+            if self._types[i] == ValueType.CONTINUOUS:
                 return False, 0
+            elif self._types[i] == ValueType.GRID:
+                number *= len(self._regions[i])
             else:
                 number *= self._regions[i][1] - self._regions[i][0] + 1
         return True, number
diff --git a/zoopt/exp_opt.py b/zoopt/exp_opt.py
index 5f505e3..c8ae505 100644
--- a/zoopt/exp_opt.py
+++ b/zoopt/exp_opt.py
@@ -4,10 +4,10 @@
 Author:
     Yu-Ren Liu
 """
-from zoopt.utils.zoo_global import gl
-from zoopt.opt import Opt
+from .utils.zoo_global import gl
+from .opt import Opt
 import matplotlib.pyplot as plt
-from zoopt.utils.tool_function import ToolFunction
+from .utils.tool_function import ToolFunction
 import numpy as np
 
 
diff --git a/zoopt/objective.py b/zoopt/objective.py
index 0a8ebb9..f61803a 100644
--- a/zoopt/objective.py
+++ b/zoopt/objective.py
@@ -5,9 +5,10 @@
     Yu-Ren Liu, Xiong-Hui Chen
 """
 
-from zoopt.solution import Solution
-from zoopt.utils.zoo_global import pos_inf
-from zoopt.utils.tool_function import ToolFunction
+from .solution import Solution
+from .utils.zoo_global import pos_inf
+from .utils.tool_function import ToolFunction
+from copy import deepcopy
 import numpy as np
 
 
@@ -27,6 +28,7 @@ def __init__(self, func=None, dim=None, constraint=None, resample_func=None):
         """
         self.__func = func
         self.__dim = dim
+        self.__dim_type = dim.get_types()
         # the function for inheriting solution attachment
         self.__inherit = self.default_inherit
         self.__post_inherit = self.default_post_inherit
@@ -136,6 +138,19 @@ def set_dim(self, dim):
 
     def get_dim(self):
         return self.__dim
+    
+    def shrink_dim(self, center, dim, shrinking_rate, shrinking_times):
+        for i in range(self.__dim.get_size()):
+            dim_reg = deepcopy(dim.get_region(i))
+            dim_reg_size = dim_reg[1] - dim_reg[0]
+            if self.__dim_type[i] is True:
+                left = max(dim_reg[0], center[i]-shrinking_rate**shrinking_times*0.5*dim_reg_size)
+                right = min(dim_reg[1], center[i]+shrinking_rate**shrinking_times*0.5*dim_reg_size)
+                self.__dim.set_region(i, [left, right])
+            else:
+                left = max(dim_reg[0], center[i]-int(shrinking_rate**shrinking_times*0.5*dim_reg_size))
+                right = min(dim_reg[1], center[i]+int(shrinking_rate**shrinking_times*0.5*dim_reg_size))
+                self.__dim.set_region(i, [left, right], ty=False)
 
     def set_inherit_func(self, inherit_func):
         self.__inherit = inherit_func
diff --git a/zoopt/opt.py b/zoopt/opt.py
index 44650ea..9db9afd 100644
--- a/zoopt/opt.py
+++ b/zoopt/opt.py
@@ -4,12 +4,11 @@
 Author:
     Yu-Ren Liu
 """
-from zoopt.algos.opt_algorithms.paretoopt.pareto_optimization import ParetoOptimization
-
-from zoopt.algos.high_dimensionality_handling.sre_optimization import SequentialRandomEmbedding
-from zoopt.algos.opt_algorithms.racos.racos_optimization import RacosOptimization
-from zoopt.utils.tool_function import ToolFunction
-from zoopt.utils.zoo_global import gl
+from .algos.opt_algorithms.paretoopt.pareto_optimization import ParetoOptimization
+from .algos.high_dimensionality_handling.sre_optimization import SequentialRandomEmbedding
+from .algos.opt_algorithms.racos.racos_optimization import RacosOptimization
+from .utils.tool_function import ToolFunction
+from .utils.zoo_global import gl
 
 
 class Opt:
@@ -37,7 +36,7 @@ def min(objective, parameter):
         result = None
         if constraint is not None and ((algorithm is None) or (algorithm == "poss")):
             optimizer = ParetoOptimization()
-        elif constraint is None and ((algorithm is None) or (algorithm == "racos") or (algorithm == "sracos")) or (algorithm == "ssracos"):
+        elif constraint is None and ((algorithm is None) or (algorithm == "racos") or (algorithm == "sracos")) or (algorithm == "racecars") or (algorithm == "ssracos"):
             optimizer = RacosOptimization()
         else:
             ToolFunction.log(
diff --git a/zoopt/parameter.py b/zoopt/parameter.py
old mode 100755
new mode 100644
index f535598..fc21fff
--- a/zoopt/parameter.py
+++ b/zoopt/parameter.py
@@ -7,8 +7,8 @@
 
 import sys
 import math
-from zoopt.dimension import Dimension
-from zoopt.utils.tool_function import ToolFunction
+from .dimension import Dimension
+from .utils.tool_function import ToolFunction
 
 
 class DefaultStoppingCriterion:
@@ -42,7 +42,8 @@ def __init__(self, algorithm=None, budget=0, exploration_rate=0.01, init_samples
                  noise_handling=False, resampling=False, suppression=False, ponss=False, ponss_theta=None, ponss_b=None,
                  non_update_allowed=500, resample_times=100, balance_rate=0.5, high_dim_handling=False, reducedim=False,
                  num_sre=5, low_dimension=None, withdraw_alpha=Dimension(1, [[-1, 1]], [True]), variance_A=None,
-                 stopping_criterion=DefaultStoppingCriterion(), seed=None, parallel=False, server_num=1):
+                 stopping_criterion=DefaultStoppingCriterion(), seed=None, parallel=False, server_num=1,
+                 shrinking_rate=None, shrinking_freq=None, max_shrinking_times=None):
         """
         Initialization.
 
@@ -88,6 +89,10 @@ def __init__(self, algorithm=None, budget=0, exploration_rate=0.01, init_samples
         :param parallel: whether to use parallel optimization.
         :param server_num: the number of servers started for parallel optimization.
 
+        :param shrinking_rate: the shrinking rate of region shrinking method
+        :param shrinking_freq: the frequency of shrinking
+        :param max_shrinking_times: the maximum shrinking times
+
         """
         self.__algorithm = algorithm
         self.__budget = budget if noise_handling is False or resampling is False else (budget / resample_times)
@@ -106,6 +111,12 @@ def __init__(self, algorithm=None, budget=0, exploration_rate=0.01, init_samples
         self.__negative_size = 0
         self.__probability = 1 - exploration_rate
 
+        # for region shrinking
+        self.__region_shrinking = True if shrinking_rate is not None else False
+        self.__shrinking_rate = shrinking_rate
+        self.__shrinking_freq = shrinking_freq
+        self.__max_shrinking_times = max_shrinking_times
+
         # for intermediate result
         self.__intermediate_result = intermediate_result
         tmp_freq = math.floor(intermediate_freq)
@@ -340,4 +351,29 @@ def get_server_num(self):
         return self.server_num
 
     def set_server_num(self, server_num):
-        self.server_num = server_num
\ No newline at end of file
+        self.server_num = server_num
+
+    def get_shrinking_rate(self):
+        return self.__shrinking_rate
+    
+    def set_shrinking_rate(self, shrinking_rate):
+        self.__shrinking_rate = shrinking_rate
+
+    def get_shrinking_freq(self):
+        return self.__shrinking_freq
+    
+    def set_shrinking_freq(self, shrinking_freq):
+        self.__shrinking_freq = shrinking_freq
+
+    def set_region_shrinking(self, region_shrinking):
+        self.__region_shrinking = region_shrinking
+
+    def get_region_shrinking(self):
+        return self.__region_shrinking
+
+    def get_max_shrinking_times(self):
+        return self.__max_shrinking_times
+    
+    def set_max_shrinking_times(self, max_shrinking_times):
+        self.__max_shrinking_times = max_shrinking_times
+    
\ No newline at end of file
diff --git a/zoopt/solution.py b/zoopt/solution.py
index a61887d..3e5b4ee 100644
--- a/zoopt/solution.py
+++ b/zoopt/solution.py
@@ -3,12 +3,15 @@
 
 Author:
     Yu-Ren Liu, Xiong-Hui Chen
+
+Updated by:
+    Ze-Wen Li
 """
 import copy
 
 import numpy as np
-from zoopt.utils.tool_function import ToolFunction
-from zoopt.utils.zoo_global import pos_inf, neg_inf, nan, gl
+from .utils.tool_function import ToolFunction
+from .utils.zoo_global import pos_inf, neg_inf, nan, gl
 
 
 class Solution:
@@ -82,14 +85,25 @@ def is_equal(self, sol):
         """
         sol_x = sol.get_x()
         sol_value = sol.get_value()
-        if sol_value != nan and self.__value != nan:
-            if abs(self.__value - sol_value) > gl.precision:
+        if sol_value is not nan and self.__value is not nan:
+            if abs(self.__value - sol_value) >= gl.precision:
                 return False
         if len(self.__x) != len(sol_x):
             return False
         for i in range(len(self.__x)):
-            if abs(self.__x[i] - sol_x[i]) > gl.precision:
+            if not type(self.__x[i]) == type(sol_x[i]):
                 return False
+            else:
+                if gl.float_precisions:  # for Dimension2 class
+                    if gl.float_precisions[i] is not None:  # CONTINUOUS
+                        if abs(self.__x[i] - sol_x[i]) >= pow(10, -1 * gl.float_precisions[i]):
+                            return False
+                    else:  # DISCRETE or GRID
+                        if not self.__x[i] == sol_x[i]:
+                            return False
+                else:  # for Dimension class
+                    if abs(self.__x[i] - sol_x[i]) >= gl.precision:
+                        return False
         return True
 
     def exist_equal(self, sol_set):