def classical_exactcover_solver(A, w=None, num_threads=4):
    nrows, ncolumns = np.shape(A)
    if w is None:
        w = np.ones(ncolumns)
    assert(len(w) == ncolumns)
    assert(sum(w >= 0))
    model = CyLPModel()
    # Decision variables, one for each cover
    x = model.addVariable('x', ncolumns, isInt=True)
    # Adding the box contraints
    model += 0 <= x <= 1
    # Adding the cover constraints
    # Sum_j x_ij ==  1
    for i in range(nrows):
        model += CyLPArray(A[i,:]) * x == 1
    # Adding the objective function
    model.objective = CyLPArray(w) * x
    lp = CyClpSimplex(model)
    lp.logLevel = 0
    lp.optimizationDirection = 'min'
    mip = lp.getCbcModel()
    mip.logLevel = 0
    # Setting number of threads
    mip.numberThreads = num_threads
    mip.solve()

    return mip.objectiveValue, [int(i) for i in mip.primalVariableSolution['x']]
def branch_and_bound(G, num_threads=4):
    N = len(G)
    model = CyLPModel()
    # Decision variables, one for each node
    x = model.addVariable('x', N, isInt=True)
    # Adjacency matrix (possibly weighted)
    W = nx.to_numpy_matrix(G)
    z_ind = dict()
    ind = 0
    w = []
    for i in range(N):
        j_range = range(N)
        if (not nx.is_directed(G)):
            # Reduced range for undirected graphs
            j_range = range(i, N)
        for j in j_range:
            if (W[i,j] == 0):
                continue
            if (i not in z_ind):
                z_ind[i] = dict()  
            z_ind[i][j] = ind
            w.append(W[i,j])
            ind += 1
    # Aux variables, one for each edge
    z = model.addVariable('z', len(w), isInt=True)
    # Adding the box contraints
    model += 0 <= x <= 1
    model += 0 <= z <= 1
    # Adding the cutting constraints
    # If x_i == x_j then z_ij = 0
    # If x_i != x_j then z_ij = 1
    for i in z_ind:
        for j in z_ind[i]:
            model += z[z_ind[i][j]] - x[i] - x[j] <= 0
            model += z[z_ind[i][j]] + x[i] + x[j] <= 2
    # Adding the objective function
    model.objective = CyLPArray(w) * z
    lp = CyClpSimplex(model)
    lp.logLevel = 0
    lp.optimizationDirection = 'max'
    mip = lp.getCbcModel()
    mip.logLevel = 0
    # Setting number of threads
    mip.numberThreads = num_threads
    mip.solve()

    return mip.objectiveValue, [int(i) for i in mip.primalVariableSolution['x']]
Beispiel #3
0
    def test_multiDim_Cbc_solve(self):
        from cylp.cy import CyClpSimplex
        from cylp.py.modeling.CyLPModel import CyLPArray
        s = CyClpSimplex()
        x = s.addVariable('x', (5, 3, 6))
        s += 2 * x[2, :, 3].sum() + 3 * x[0, 1, :].sum() >= 5.5
        s += 0 <= x <= 2.2
        c = CyLPArray(list(range(18)))
        s.objective = c * x[2, :, :] + c * x[0, :, :]

        s.setInteger(x)

        cbcModel = s.getCbcModel()
        cbcModel.solve()

        sol_x = cbcModel.primalVariableSolution['x']
        self.assertTrue(abs(sol_x[0, 1, 0] - 1) <= 10**-6)
        self.assertTrue(abs(sol_x[2, 0, 3] - 2) <= 10**-6)
Beispiel #4
0
    def test_multiDim_Cbc_solve(self):
        from cylp.cy import CyClpSimplex
        from cylp.py.modeling.CyLPModel import CyLPArray
        s = CyClpSimplex()
        x = s.addVariable('x', (5, 3, 6))
        s += 2 * x[2, :, 3].sum() + 3 * x[0, 1, :].sum() >= 5.5
        s += 0 <= x <= 2.2
        c = CyLPArray(range(18))
        s.objective = c * x[2, :, :] + c * x[0, :, :]

        s.setInteger(x)

        cbcModel = s.getCbcModel()
        cbcModel.solve()

        sol_x = cbcModel.primalVariableSolution['x']
        self.assertTrue(abs(sol_x[0, 1, 0] - 1) <= 10**-6)
        self.assertTrue(abs(sol_x[2, 0, 3] - 2) <= 10**-6)
Beispiel #5
0
    def test_NodeCompare(self):
        s = CyClpSimplex()
        s.readMps(join(currentFilePath, '../input/p0033.mps'))

        s.copyInIntegerInformation(np.array(s.nCols * [True], np.uint8))

        print("Solving relaxation")
        cbcModel = s.getCbcModel()
        n = SimpleNodeCompare()
        cbcModel.setNodeCompare(n)

        gom = CyCglGomory(limit=150)
        #gom.limit = 150
        cbcModel.addCutGenerator(gom, name="Gomory")

        #clq = CyCglClique()
        #cbcModel.addCutGenerator(clq, name="Clique")

        knap = CyCglKnapsackCover(maxInKnapsack=50)
        cbcModel.addCutGenerator(knap, name="Knapsack")

        cbcModel.branchAndBound()
        self.assertTrue(abs(cbcModel.objectiveValue - 3089.0) < 10**-6)
Beispiel #6
0
    def test_NodeCompare(self):
        s = CyClpSimplex()
        s.readMps(join(currentFilePath, '../input/p0033.mps'))

        s.copyInIntegerInformation(np.array(s.nCols * [True], np.uint8))

        print "Solving relaxation"
        cbcModel = s.getCbcModel()
        n = SimpleNodeCompare()
        cbcModel.setNodeCompare(n)

        gom = CyCglGomory(limit=150)
        #gom.limit = 150
        cbcModel.addCutGenerator(gom, name="Gomory")

        #clq = CyCglClique()
        #cbcModel.addCutGenerator(clq, name="Clique")

        knap = CyCglKnapsackCover(maxInKnapsack=50)
        cbcModel.addCutGenerator(knap, name="Knapsack")


        cbcModel.branchAndBound()
        self.assertTrue(abs(cbcModel.objectiveValue - 3089.0) < 10 ** -6)
Beispiel #7
0
    if (firstExample):
        x = m.addVariable('x', 2, isInt=True)

        A = np.matrix([[7., -2.], [0., 1], [2., -2]])
        b = CyLPArray([14, 3, 3])

        m += A * x <= b
        m += x >= 0

        c = CyLPArray([-4, 1])
        m.objective = c * x
        s = CyClpSimplex(m)
    else:
        s = CyClpSimplex()
        #cylpDir = os.environ['CYLP_SOURCE_DIR']
        inputFile = os.path.join('..', '..', 'input', 'p0033.mps')
        m = s.extractCyLPModel(inputFile)
        x = m.getVarByName('x')
        s.setInteger(x)

    cbcModel = s.getCbcModel()

    gc = GomoryCutGenerator(m)
    #cbcModel.addPythonCutGenerator(gc, name='PyGomory')

    #cbcModel.branchAndBound()
    cbcModel.solve()

    print(cbcModel.primalVariableSolution)
Beispiel #8
0
    def solve(self, objective, constraints, cached_data, warm_start, verbose,
              solver_opts):
        """Returns the result of the call to the solver.

        Parameters
        ----------
        objective : LinOp
            The canonicalized objective.
        constraints : list
            The list of canonicalized cosntraints.
        cached_data : dict
            A map of solver name to cached problem data.
        warm_start : bool
            Not used.
        verbose : bool
            Should the solver print output?
        solver_opts : dict
            Additional arguments for the solver.

        Returns
        -------
        tuple
            (status, optimal value, primal, equality dual, inequality dual)
        """
        # Import basic modelling tools of cylp
        from cylp.cy import CyClpSimplex

        # Get problem data
        data = self.get_problem_data(objective, constraints, cached_data)

        c = data[s.C]
        b = data[s.B]
        A = data[s.A]
        dims = data[s.DIMS]

        n = c.shape[0]

        # Problem
        model = CyClpSimplex()

        # Variables
        x = model.addVariable('x', n)

        if self.is_mip(data):
            for i in data[s.BOOL_IDX]:
                model.setInteger(x[i])
            for i in data[s.INT_IDX]:
                model.setInteger(x[i])

        # Constraints
        # eq
        model += A[0:dims[s.EQ_DIM], :] * x == b[0:dims[s.EQ_DIM]]

        # leq
        leq_start = dims[s.EQ_DIM]
        leq_end = dims[s.EQ_DIM] + dims[s.LEQ_DIM]
        model += A[leq_start:leq_end, :] * x <= b[leq_start:leq_end]

        # no boolean vars available in cbc -> model as int + restrict to [0,1]
        if self.is_mip(data):
            for i in data[s.BOOL_IDX]:
                model += 0 <= x[i] <= 1

        # Objective
        model.objective = c

        # Build model & solve
        status = None
        if self.is_mip(data):
            cbcModel = model.getCbcModel()  # need to convert model
            if not verbose:
                cbcModel.logLevel = 0

            # Add cut-generators (optional)
            for cut_name, cut_func in six.iteritems(
                    self.SUPPORTED_CUT_GENERATORS):
                if cut_name in solver_opts and solver_opts[cut_name]:
                    module = importlib.import_module("cylp.cy.CyCgl")
                    funcToCall = getattr(module, cut_func)
                    cut_gen = funcToCall()
                    cbcModel.addCutGenerator(cut_gen, name=cut_name)

            # solve
            status = cbcModel.branchAndBound()
        else:
            if not verbose:
                model.logLevel = 0
            status = model.primal()  # solve

        results_dict = {}
        results_dict["status"] = status

        if self.is_mip(data):
            results_dict["x"] = cbcModel.primalVariableSolution['x']
            results_dict["obj_value"] = cbcModel.objectiveValue
        else:
            results_dict["x"] = model.primalVariableSolution['x']
            results_dict["obj_value"] = model.objectiveValue

        return self.format_results(results_dict, data, cached_data)
Beispiel #9
0
    def solve(self, objective, constraints, cached_data,
              warm_start, verbose, solver_opts):
        """Returns the result of the call to the solver.

        Parameters
        ----------
        objective : LinOp
            The canonicalized objective.
        constraints : list
            The list of canonicalized cosntraints.
        cached_data : dict
            A map of solver name to cached problem data.
        warm_start : bool
            Not used.
        verbose : bool
            Should the solver print output?
        solver_opts : dict
            Additional arguments for the solver.

        Returns
        -------
        tuple
            (status, optimal value, primal, equality dual, inequality dual)
        """
        # Import basic modelling tools of cylp
        from cylp.cy import CyClpSimplex
        from cylp.py.modeling.CyLPModel import CyLPArray

        # Get problem data
        data = self.get_problem_data(objective, constraints, cached_data)

        c = data[s.C]
        b = data[s.B]
        A = data[s.A]
        dims = data[s.DIMS]

        n = c.shape[0]

        solver_cache = cached_data[self.name()]

        # Problem
        model = CyClpSimplex()

        # Variables
        x = model.addVariable('x', n)

        if self.is_mip(data):
            for i in data[s.BOOL_IDX]:
                model.setInteger(x[i])
            for i in data[s.INT_IDX]:
                model.setInteger(x[i])

        # Constraints
        # eq
        model += A[0:dims[s.EQ_DIM], :] * x == b[0:dims[s.EQ_DIM]]

        # leq
        leq_start = dims[s.EQ_DIM]
        leq_end = dims[s.EQ_DIM] + dims[s.LEQ_DIM]
        model += A[leq_start:leq_end, :] * x <= b[leq_start:leq_end]

        # no boolean vars available in cbc -> model as int + restrict to [0,1]
        if self.is_mip(data):
            for i in data[s.BOOL_IDX]:
                model += 0 <= x[i] <= 1

        # Objective
        model.objective = c

        # Build model & solve
        status = None
        if self.is_mip(data):
            cbcModel = model.getCbcModel()  # need to convert model
            if not verbose:
                cbcModel.logLevel = 0

            # Add cut-generators (optional)
            for cut_name, cut_func in six.iteritems(self.SUPPORTED_CUT_GENERATORS):
                if cut_name in solver_opts and solver_opts[cut_name]:
                    module = importlib.import_module("cylp.cy.CyCgl")
                    funcToCall = getattr(module, cut_func)
                    cut_gen = funcToCall()
                    cbcModel.addCutGenerator(cut_gen, name=cut_name)

            # solve
            status = cbcModel.branchAndBound()
        else:
            if not verbose:
                model.logLevel = 0
            status = model.primal()  # solve

        results_dict = {}
        results_dict["status"] = status

        if self.is_mip(data):
            results_dict["x"] = cbcModel.primalVariableSolution['x']
            results_dict["obj_value"] = cbcModel.objectiveValue
        else:
            results_dict["x"] = model.primalVariableSolution['x']
            results_dict["obj_value"] = model.objectiveValue

        return self.format_results(results_dict, data, cached_data)
Beispiel #10
0
    if (firstExample):
        x = m.addVariable('x', 2, isInt=True)

        A = np.matrix([[7., -2.],[0., 1], [2., -2]])
        b = CyLPArray([14, 3, 3])

        m += A * x <= b
        m += x >= 0

        c = CyLPArray([-4, 1])
        m.objective = c * x
        s = CyClpSimplex(m)
    else:
        s = CyClpSimplex()
        cylpDir = os.environ['CYLP_SOURCE_DIR']
        inputFile = os.path.join(cylpDir, 'cylp', 'input', 'p0033.mps')
        m = s.extractCyLPModel(inputFile)
        x = m.getVarByName('x')
        s.setInteger(x)

    cbcModel = s.getCbcModel()

    gc = GomoryCutGenerator(m)
    cbcModel.addPythonCutGenerator(gc, name='PyGomory')

    cbcModel.branchAndBound()

    print cbcModel.primalVariableSolution

def classical_maxkcut_solver(G, num_partitions, num_threads=4):
    # G: NetworkX graph
    # num_partitions: the number partitions or groups in which we should
    #                 subdivide the nodes (i.e., the value of K)

    N = len(G)
    model = CyLPModel()
    # Decision variables, one for each node
    x = model.addVariable('x', num_partitions * N, isInt=True)
    # Adjacency matrix (possibly weighted)
    W = nx.to_numpy_matrix(G)
    z_ind = dict()
    ind = 0
    w = []
    for i in range(N):
        j_range = range(N)
        if (not nx.is_directed(G)):
            # Reduced range for undirected graphs
            j_range = range(i, N)
        for j in j_range:
            if (W[i, j] == 0):
                continue
            if (i not in z_ind):
                z_ind[i] = dict()
            z_ind[i][j] = ind
            w.append(W[i, j])
            ind += 1
    # Aux variables, one for each edge
    z = model.addVariable('z', len(w), isInt=True)
    # Adding the box contraints
    model += 0 <= x <= 1
    model += 0 <= z <= 1
    # Adding the selection constraints
    for i in range(N):
        indices = [i + k * N for k in range(num_partitions)]
        model += x[indices].sum() == 1
    # Adding the cutting constraints
    for i in z_ind:
        for j in z_ind[i]:
            for k in range(num_partitions):
                shift = k * N
                model += z[z_ind[i][j]] + x[i + shift] + x[j + shift] <= 2
                model += z[z_ind[i][j]] + x[i + shift] - x[j + shift] >= 0
                model += z[z_ind[i][j]] - x[i + shift] + x[j + shift] >= 0
    # Adding the objective function
    model.objective = CyLPArray(w) * z
    lp = CyClpSimplex(model)
    lp.logLevel = 0
    lp.optimizationDirection = 'max'
    mip = lp.getCbcModel()
    mip.logLevel = 0
    # Setting number of threads
    mip.numberThreads = num_threads
    mip.solve()
    sol = [int(i) for i in mip.primalVariableSolution['x']]
    sol_formatted = []
    for i in range(N):
        indices = [i + k * N for k in range(num_partitions)]
        for j in range(num_partitions):
            if (sol[indices[j]] == 1):
                sol_formatted.append(j)
                break

    assert (len(sol_formatted) == N)

    return mip.objectiveValue, sol_formatted
Beispiel #12
0
    def solve_ilp(self):
        """ Solves problem exactly using MIP/ILP approach
            Used solver: CoinOR CBC
            Incidence-matrix Q holds complete information needed for opt-process
        """
        if self.verbose:
            print('Solve: build model')

        if self.condorcet_red:
            condorcet_red_mat = extended_condorcet_simple(self.votes_arr)

        n = self.Q.shape[0]
        x_n = n * n

        model = CyClpSimplex()  # MODEL
        x = model.addVariable('x', x_n, isInt=True)  # VARS

        model.objective = self.Q.ravel()  # OBJ

        # x_ab = boolean (already int; need to constrain to [0,1])
        model += sp.eye(x_n) * x >= np.zeros(x_n)
        model += sp.eye(x_n) * x <= np.ones(x_n)

        idx = lambda i, j: np.ravel_multi_index((i, j), (n, n))

        # constraints for every pair
        start_time = time()
        n_pairwise_constr = n * (n - 1) // 2
        if self.verbose:
            print('  # pairwise constr: ', n_pairwise_constr)

        # Somewhat bloated just to get some vectorization / speed !
        combs_ = combs(range(n), 2)

        inds_a = np.ravel_multi_index(combs_.T, (n, n))
        inds_b = np.ravel_multi_index(combs_.T[::-1], (n, n))

        row_inds = np.tile(np.arange(n_pairwise_constr), 2)
        col_inds = np.hstack((inds_a, inds_b))

        pairwise_constraints = sp.coo_matrix(
            (np.ones(n_pairwise_constr * 2), (row_inds, col_inds)),
            shape=(n_pairwise_constr, n * n))
        end_time = time()
        if self.verbose:
            print("    Took {:.{prec}f} secs".format(end_time - start_time,
                                                     prec=3))

        # and for every cycle of length 3
        start_time = time()
        n_triangle_constrs = n * (n - 1) * (n - 2)
        if self.verbose:
            print('  # triangle constr: ', n_triangle_constrs)

        # Somewhat bloated just to get some vectorization / speed !
        perms_ = perms(range(n), 3)

        inds_a = np.ravel_multi_index(perms_.T[(0, 1), :], (n, n))
        inds_b = np.ravel_multi_index(perms_.T[(1, 2), :], (n, n))
        inds_c = np.ravel_multi_index(perms_.T[(2, 0), :], (n, n))

        row_inds = np.tile(np.arange(n_triangle_constrs), 3)
        col_inds = np.hstack((inds_a, inds_b, inds_c))

        triangle_constraints = sp.coo_matrix(
            (np.ones(n_triangle_constrs * 3), (row_inds, col_inds)),
            shape=(n_triangle_constrs, n * n))
        end_time = time()
        if self.verbose:
            print("    Took {:.{prec}f} secs".format(end_time - start_time,
                                                     prec=3))

        model += pairwise_constraints * x == np.ones(n_pairwise_constr)
        model += triangle_constraints * x >= np.ones(n_triangle_constrs)

        if self.condorcet_red and condorcet_red_mat != None:
            I, J, V = sp.find(condorcet_red_mat)
            indices_pos = np.ravel_multi_index([J, I], (n, n))
            indices_neg = np.ravel_multi_index([I, J], (n, n))
            nnz = len(indices_pos)

            if self.verbose:
                print(
                    '  Extended Condorcet reductions: {} * 2 relations fixed'.
                    format(nnz))

            lhs = sp.coo_matrix(
                (np.ones(nnz * 2),
                 (np.arange(nnz * 2), np.hstack((indices_pos, indices_neg)))),
                shape=(nnz * 2, n * n))
            rhs = np.hstack(
                (np.ones(len(indices_pos)), np.zeros(len(indices_neg))))
            model += lhs * x == rhs

        cbcModel = model.getCbcModel()  # Clp -> Cbc model / LP -> MIP
        cbcModel.logLevel = self.verbose

        if self.verbose:
            print('Solve: run MIP\n')
        start_time = time()
        status = cbcModel.solve()  #-> "Call CbcMain. Solve the problem
        #   "using the same parameters used
        #   "by CbcSolver."
        # This deviates from cylp's docs which are sparse!
        # -> preprocessing will be used and is very important!
        end_time = time()
        if self.verbose:
            print("  CoinOR CBC used {:.{prec}f} secs".format(end_time -
                                                              start_time,
                                                              prec=3))

        x_sol = cbcModel.primalVariableSolution['x']
        self.obj_sol = cbcModel.objectiveValue
        x = np.array(x_sol).reshape((n, n)).round().astype(int)
        self.aggr_rank = np.argsort(x.sum(axis=0))[::-1]
Beispiel #13
0
def tournament(args):
    # Configuration
    engine = create_engine('sqlite:///' + args.bdd, echo=False)
    DBSession.configure(bind=engine)

    # On récupere les donnes de la BDD
    students = DBSession.query(Etudiant).filter(Etudiant.enseignant.is_(None))
    nb_students = DBSession.query(Etudiant).filter(
        Etudiant.enseignant.is_(None)).count()
    nb_parcours = DBSession.query(Parcours).count()

    # Capacité des groupes
    capa_min_pec = 14
    capa_max_pec = 16
    capa_min_pel = 14
    capa_max_pel = 16

    # On construit le modèle
    barre_min = args.b
    model = CyClpSimplex()
    x = []
    s_list = {}
    for s in students.all():
        v = model.addVariable('etu' + str(s.id), nb_parcours, isInt=True)
        x.append(v)

    z = 0.0
    i = 0
    for s in students.all():
        reorder = False
        s_list[str(i)] = s.id
        voeux = DBSession.query(Voeu).filter(Voeu.idEtudiant == s.id).order_by(
            Voeu.idParcours).all()
        s_voeux = []
        rang_q = voeux[nb_parcours - 1].rang
        if rang_q != nb_parcours and rang_q != -1:
            if s.nom not in special or 'quebec' not in special[s.nom]:
                reorder = True
            else:
                reorder = not special[s.nom]['quebec']
        for v in voeux:
            if v.rang == -1:
                score = 0
            else:
                if reorder and v.rang > rang_q:
                    rang = v.rang - 1
                elif reorder and v.rang == rang_q:
                    rang = 9
                else:
                    rang = v.rang
                malus = 0.0
                if s.malus > 0:
                    malus += s.malus
                if s.absences is not None:
                    malus += float(s.absences) / 15
                score = 2.0**(rang - malus)
            s_voeux.append(score)
        a = CyLPArray(s_voeux)
        b = CyLPArray([1.0 for j in range(nb_parcours)])
        z += a * x[i]
        model += b * x[i] >= 1
        model += b * x[i] <= 1
        model += x[i] >= 0
        model += x[i] <= 1
        i += 1
    model.objective = z

    # Taille des groupes MpInge (PEL)
    for j in [
            0, 3, 6
    ]:  # Attention, dans la BDD, le parcours vont de 1 à 8 -> décalage de 1
        c = 0
        for i in range(nb_students):
            c += x[i][j]
        model += c <= capa_max_pel
        model += c >= capa_min_pel

    # Taille des groupes PEC
    for j in [1, 2, 4, 5, 7]:
        c = 0
        for i in range(nb_students):
            c += x[i][j]
        model += c <= capa_max_pec
        model += c >= capa_min_pec

    # Etudiants trop bas en maths
    i = 0
    for s in students.all():
        if s.moyenneMaths is not None and s.moyenneMaths < barre_min:
            model += x[i][0] + x[i][3] + x[i][6] == 0
        i += 1

    # Québec
    i = 0
    for s in students.all():
        if s.nom not in special or 'quebec' not in special[s.nom]:
            model += x[i][nb_parcours - 1] == 0
        i += 1

    # Cas particuliers (cf special.py)
    i = 0
    for s in students.all():
        if s.nom in special and 'force' in special[s.nom]:
            for cas in special[s.nom]['force']:
                if not special[s.nom]['force'][cas]:
                    model += x[i][int(cas) - 1] == 0
        i += 1

    cbc_model = model.getCbcModel()
    cbc_model.logLevel = 0
    cbc_model.branchAndBound()

    if cbc_model.isRelaxationOptimal() and args.v:
        for s in students.all():
            r = cbc_model.primalVariableSolution['etu' + str(s.id)]
            print s.nom + '|',
            j = 1
            reorder = False
            for res in r:
                if res == 1:
                    if j == 1 or j == 4 or j == 7:
                        print "!",
                    par = DBSession.query(Parcours).filter(
                        Parcours.id == j).one()
                    print par.nom,
                    voeux = DBSession.query(Voeu).filter(
                        Voeu.idEtudiant == s.id).order_by(
                            Voeu.idParcours).all()
                    rang_q = voeux[nb_parcours - 1].rang
                    if rang_q != nb_parcours and rang_q != -1:
                        if s.nom not in special or 'quebec' not in special[
                                s.nom]:
                            reorder = True
                        else:
                            reorder = not special[s.nom]['quebec']
                    son_voeu = DBSession.query(Voeu).filter(
                        Voeu.idEtudiant == s.id).filter(
                            Voeu.idParcours == j).one()
                    if reorder and son_voeu.rang == rang_q:
                        rang = 9
                    elif reorder and son_voeu.rang > rang_q:
                        rang = son_voeu.rang - 1
                    else:
                        rang = son_voeu.rang
                    print '|' + str(rang) + '',
                    print '|' + str(s.moyenneMaths),
                    print '|' + str(s.absences),
                    if reorder:
                        print '|*'
                    else:
                        print '|'
                j += 1

    nb_stu_grp = [0 for n in range(nb_parcours)]
    rangs_stu = [0 for n in range(nb_parcours)]
    indecis = 0
    pec2pel = 0
    pel2pec = 0

    if cbc_model.isRelaxationOptimal() and (args.s or args.csv):
        for s in students.all():
            r = cbc_model.primalVariableSolution['etu' + str(s.id)]
            j = 1
            reorder = False
            for res in r:
                if res == 1:
                    nb_stu_grp[j - 1] += 1
                    voeux = DBSession.query(Voeu).filter(
                        Voeu.idEtudiant == s.id).order_by(
                            Voeu.idParcours).all()
                    rang_q = voeux[nb_parcours - 1].rang
                    if rang_q != nb_parcours and rang_q != -1:
                        if s.nom not in special or 'quebec' not in special[
                                s.nom]:
                            reorder = True
                        else:
                            reorder = not special[s.nom]['quebec']
                    son_voeu = DBSession.query(Voeu).filter(
                        Voeu.idEtudiant == s.id).filter(
                            Voeu.idParcours == j).one()
                    if reorder and son_voeu.rang == rang_q:
                        rang = 9
                    elif reorder and son_voeu.rang > rang_q:
                        rang = son_voeu.rang - 1
                    else:
                        rang = son_voeu.rang
                    if rang != -1:
                        rangs_stu[rang - 1] += 1
                        son_voeu_un = DBSession.query(Voeu).filter(
                            Voeu.idEtudiant == s.id).filter(
                                Voeu.rang == 1).one()
                        if son_voeu_un.idParcours in [
                                1, 4, 7
                        ] and son_voeu.idParcours not in [1, 4, 7]:
                            pel2pec += 1
                        elif son_voeu_un.idParcours not in [
                                1, 4, 7
                        ] and son_voeu.idParcours in [1, 4, 7]:
                            pec2pel += 1
                    else:
                        indecis += 1
                j += 1

        if args.s:
            print "Etudiants par groupe : ", nb_stu_grp
            print "Etudiants par rang de voeu : ", rangs_stu
            print "Passages PEC->PEL", pec2pel
            print "Passage PEL->PEC", pel2pec
            print "Indécis : ", indecis

        if args.csv:
            print barre_min, ",",
            for item in nb_stu_grp:
                print item, ",",
            for item in rangs_stu:
                print item, ",",
            print pec2pel, ",", pel2pec

    if cbc_model.isRelaxationInfeasible():
        print "Pas de solution possible"