ubqp_example.rst 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. ==========================================
  2. Unconstrained Binary Quadratic Programming
  3. ==========================================
  4. Given a collection of :math:`n` items such that each pair of items is associated with a profit value that can be positive, negative or zero, unconstrained binary quadratic programming (UBQP) seeks a subset of items that maximizes the sum of their paired values. The value of a pair is accumulated in the sum only if the two corresponding items are selected.
  5. The UBQP problem will be tackle in this example.
  6. UBQP problem definition
  7. =======================
  8. Understand the UBQP Problem
  9. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10. Given a collection of :math:`n` items such that each pair of items is associated with a profit value that can be positive, negative or zero, unconstrained binary quadratic programming (UBQP) seeks a subset of items that maximizes the sum of their paired values. The value of a pair is accumulated in the sum only if the two corresponding items are selected. A feasible solution to a UBQP instance can be specified by a binary string of size :math:`n`, such that each variable indicates whether the corresponding item is included in the selection or not.
  11. Mathematical definition
  12. ~~~~~~~~~~~~~~~~~~~~~~~
  13. More formally, the conventional and single-objective UBQP problem is to maximize the following objective function:
  14. :math:`f(x)=x′Qx=\sum_{i=1}^{n}{\sum_{j=1}^{n}{q_{ij}⋅x_i⋅x_j}}`
  15. where :math:`Q=(q_{ij})` is an :math:`n` by :math:`n` matrix of constant values, :math:`x` is a vector of :math:`n` binary (zero-one) variables, i.e., :math:`x \in \{0, 1\}`, :math:`i \in \{1,...,n\}`, and :math:`x'` is the transpose of :math:`x`.
  16. UBQP Problem instance generation
  17. ================================
  18. To define our quadratic assignment problem instance, we will use the available mUBQP_ multi-objective quadratic problem generator.
  19. Genration of the instance
  20. ~~~~~~~~~~~~~~~~~~~~~~~~~
  21. We will limit ourselves here to a single objective for the purposes of this example. The available file **mubqpGenerator.R**, will be used to generate the instance (using R language).
  22. .. code:: bash
  23. Rscript mubqpGenerator.R 0.8 1 100 5 42 ubqp_instance.txt
  24. The main parameters used for generating our UBQP instance are:
  25. - **ρ:** the objective correlation coefficient
  26. - **M:** the number of objective functions
  27. - **N:** the length of bit strings
  28. - **d:** the matrix density (frequency of non-zero numbers)
  29. - **s:** seed to use
  30. .. _mUBQP: http://mocobench.sourceforge.net/index.php?n=Problem.MUBQP
  31. .. _ubqp_instance.txt: https://github.com/jbuisine/macop/blob/master/examples/instances/ubqp/ubqp_instance.txt
  32. Load data instance
  33. ~~~~~~~~~~~~~~~~~~
  34. We are now going to load this instance via a Python code which will be useful to us later on:
  35. .. code:: Python
  36. qap_instance_file = 'ubqp_instance.txt'
  37. n = 100 # the instance size
  38. # load UBQP instance
  39. with open(ubqp_instance_file, 'r') as f:
  40. lines = f.readlines()
  41. # get all string floating point values of matrix
  42. Q_data = ''.join([ line.replace('\n', '') for line in lines[8:] ])
  43. # load the concatenate obtained string
  44. Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
  45. print(f'Q_matrix {Q_matrix.shape}')
  46. .. note::
  47. As we know the size of our instance and the structure of the document (header size), it is quite quick to look for the lines related to the :math:`Q` matrix.
  48. Macop UBQP implementation
  49. =========================
  50. Let's see how it is possible with the use of the **Macop** package to implement and deal with this UBQP instance problem.
  51. Solution structure definition
  52. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  53. Firstly, we are going to use a type of solution that will allow us to define the structure of our solutions.
  54. The available macop.solutions.discrete.BinarySolution_ type of solution within the Macop package represents exactly what one would wish for.
  55. Let's see an example of its use:
  56. .. code:: python
  57. from macop.solutions.discrete import BinarySolution
  58. solution = BinarySolution.random(10)
  59. print(solution)
  60. The resulting solution obtained:
  61. .. code:: bash
  62. Binary solution [1 0 1 1 1 0 0 1 1 0]
  63. UBQP Evaluator
  64. ~~~~~~~~~~~~~~
  65. Now that we have the structure of our solutions, and the means to generate them, we will seek to evaluate them.
  66. To do this, we need to create a new evaluator specific to our problem and the relative evaluation function we need to maximise:
  67. - :math:`f(x)=x′Qx=\sum_{i=1}^{n}{\sum_{j=1}^{n}{q_{ij}⋅x_i⋅x_j}}`
  68. So we are going to create a class that will inherit from the abstract class macop.evaluators.base.Evaluator_:
  69. .. code:: python
  70. from macop.evaluators.base import Evaluator
  71. class UBQPEvaluator(Evaluator):
  72. """UBQP evaluator class which enables to compute UBQP solution using specific `_data`
  73. - stores into its `_data` dictionary attritute required measures when computing a UBQP solution
  74. - `_data['Q']` matrix of size n x n with real values data (stored as numpy array)
  75. - `compute` method enables to compute and associate a score to a given UBQP solution
  76. """
  77. def compute(self, solution):
  78. """Apply the computation of fitness from solution
  79. Args:
  80. solution: {Solution} -- UBQP solution instance
  81. Returns:
  82. {float} -- fitness score of solution
  83. """
  84. fitness = 0
  85. for index_i, val_i in enumerate(solution.getdata = )):
  86. for index_j, val_j in enumerate(solution.getdata = )):
  87. fitness += self._data['Q'][index_i, index_j] * val_i * val_j
  88. return fitness
  89. The cost function for the Unconstrained binary quadratic problem is now well defined.
  90. .. tip::
  91. The class proposed here, is available in the Macop package macop.evaluators.discrete.mono.UBQPEvaluator_.
  92. Running algorithm
  93. ~~~~~~~~~~~~~~~~~
  94. Now that the necessary tools are available, we will be able to deal with our problem and look for solutions in the search space of our UBQP instance.
  95. Here we will use local search algorithms already implemented in **Macop**.
  96. If you are uncomfortable with some of the elements in the code that will follow, you can refer to the more complete **Macop** documentation_ that focuses more on the concepts and tools of the package.
  97. .. code:: python
  98. # main imports
  99. import numpy as np
  100. # module imports
  101. from macop.solutions.discrete import BinarySolution
  102. from macop.evaluators.discrete.mono import UBQPEvaluator
  103. from macop.operators.discrete.mutators import SimpleMutation, SimpleBinaryMutation
  104. from macop.policies.classicals import RandomPolicy
  105. from macop.algorithms.mono import IteratedLocalSearch as ILS
  106. from macop.algorithms.mono import HillClimberFirstImprovment
  107. # usefull instance data
  108. n = 100
  109. ubqp_instance_file = 'ubqp_instance.txt'
  110. # default validator
  111. def validator(solution):
  112. return True
  113. # define init random solution
  114. def init():
  115. return BinarySolution.random(n, validator)
  116. # load UBQP instance
  117. with open(ubqp_instance_file, 'r') as f:
  118. lines = f.readlines()
  119. # get all string floating point values of matrix
  120. Q_data = ''.join([ line.replace('\n', '') for line in lines[8:] ])
  121. # load the concatenate obtained string
  122. Q_matrix = np.fromstring(Q_data, dtype=float, sep=' ').reshape(n, n)
  123. print(f'Q_matrix shape: {Q_matrix.shape}')
  124. # only one operator here
  125. operators = [SimpleMutation(), SimpleBinaryMutation()]
  126. # random policy
  127. policy = RandomPolicy(operators)
  128. # use of loaded data from UBQP instance
  129. evaluator = UBQPEvaluator(data={'Q': Q_matrix})
  130. # passing global evaluation param from ILS
  131. hcfi = HillClimberFirstImprovment(init, evaluator, operators, policy, validator, maximise=True, verbose=True)
  132. algo = ILS(init, evaluator, operators, policy, validator, localSearch=hcfi, maximise=True, verbose=True)
  133. # run the algorithm
  134. bestSol = algo.run(10000, ls_evaluations=100)
  135. print('Solution for UBQP instance score is {}'.format(evaluator.compute(bestSol)))
  136. UBQP problem solving is now possible with **Macop**. As a reminder, the complete code is available in the ubqpExample.py_ file.
  137. .. _ubqpExample.py: https://github.com/jbuisine/macop/blob/master/examples/ubqpExample.py
  138. .. _documentation: https://jbuisine.github.io/macop/_build/html/documentations
  139. .. _macop.solutions.discrete.BinarySolution: macop/macop.solutions.discrete.html#macop.solutions.discrete.BinarySolution
  140. .. _macop.evaluators.base.Evaluator: macop/macop.evaluators.base.html#macop.evaluators.base.Evaluator
  141. .. _macop.evaluators.discrete.mono.UBQPEvaluator: macop/macop.evaluators.discrete.mono.html#macop.evaluators.discrete.mono.UBQPEvaluator