title: Python Pulp linear programming with dynamic constraint
body: <p>I currently use Solver in excel to find somewhat optimal solution for manufacturing. Here is the current setup:
  <a href="https://i.stack.imgur.com/7c7Cq.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/7c7Cq.png" alt="enter image description here"></a></p>
  
  <p>It regards manufacturing shoes on a rotary machine, that is, production is done in batches of repetitions. For example one batch would be '10x A1' (see A1 in table) which would yield 10x size 36, 20x size 37... 10x size 41.</p>
  
  <p>There are some prefixed setups; A1, A2; R7... as you see in the table above.</p>
  
  <p>Then there is the <code>requested</code> variable (or rather a list of variables) that basically says what the customer requested, quantities per size.</p>
  
  <p>The <strong>objective function</strong> is to find a set of repetitions such that it matches requested quantities as closely as possible. Hence in the solver (sorry for non-English screenshot) you can see the goal is <code>N21</code> (that is the sum of absolute differences per size). The variables are <code>N2:N9</code> - that's the repetitions per setup and the only constraint is that <code>N2:N9</code> is an integer.</p>
  
  <p>How can I model this behaviour with python? My start:</p>
  
  <pre><code>from collections import namedtuple
  
  from pulp import *
  
  
  class Setup(namedtuple('IAmReallyLazy', 'name ' + ' '.join(f's{s}' for s in range(36, 47)))):
      # inits with name and sizes 's36', 's37'... 's46'
      repetitions = 0
  
  
  setups = [
      Setup('A1', 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0),
      Setup('A2', 0, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0),
      Setup('R7', 0, 0, 1, 1, 1, 1, 2, 0, 0, 0, 0),
      Setup('D1', 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
      # and others
  ]
  
  setup_names = [s.name for s in setups]
  
  requested = {
      's36': 100,
      's37': 250,
      's38': 300,
      's39': 450,
      's40': 450,
      's41': 250,
      's42': 200,
  }
  
  
  def get_quantity_per_size(size: str) -&gt; int:
      return sum([getattr(setup, size) * setup.repetitions for setup in setups])
  
  
  def get_abs_diff(size: str) -&gt; int:
      requested_size = requested.get(size, 0)
      return abs(get_quantity_per_size(size) - requested_size)
  
  
  problem = LpProblem('Optimize Batches', LpMinimize)
  # goal is to minimise the sum(get_abs_diff(f's{size}') for size in range(36, 47))
  # variables are [setup.repetitions for setup in setups]
  # constraints are all([isinstance(setup.repetitions, int) for setup in setups])
  </code></pre>
  
  <p><em>In an ideal world</em> if there is more than one optimal solution, the one with most spread abs diff should be selected (ie. the one with the smallest highest difference). That is, if one solution has abs diff of 10 per size and 10 sizes (total 100) and other has 20 + 80 = 100, the first one is more optimal for customer.</p>
  
  <p>Another constraint should be <code>min(setup.repetitions for setup in setups if setup.repetitions &gt; 0) &gt; 9</code> basically the repetitions constraint should be:</p>
  
  <ul>
  <li>Is integer</li>
  <li>Is either 0 <em>or</em> greater than 9 - This is not possible in linear programming from what I've understood so far though. </li>
  </ul>