Ejemplo n.º 1
0
    def __init__(self, quantite_objectif, liste_gallons):

        # Create a MiniZinc model
        self.model = mnz.Model()
        self.model.add_file("ProjSceau.mzn")

        # Transform Model into a instance
        gecode = mnz.Solver.lookup("gecode")
        self.inst = mnz.Instance(gecode, self.model)

        self.gallons = liste_gallons
        self.quantite_objectif = quantite_objectif

        self.inst["nbr_sceau"] = len(liste_gallons)
        self.inst["remplissage_final"] = quantite_objectif

        list_taille_sceau = []
        list_remplissage_init = []
        for g in self.gallons:
            list_taille_sceau.append(g.taille)
            list_remplissage_init.append(g.remplissage)

        self.inst["taille_sceau"] = list_taille_sceau
        self.inst["remplissage_initial"] = list_remplissage_init

        print("CONFIGURATION d'ORIGINE:")
        print("\tnombre sceaux : ", self.inst["nbr_sceau"])
        print("\tremplissage_final : ", self.inst["remplissage_final"])
        print("\ttaille_sceau : ", self.inst["taille_sceau"])
        print("\tremplissage_initial : ", self.inst["remplissage_initial"])
Ejemplo n.º 2
0
async def run_instance(problem, model, data, config, timeout, stat_base,
                       sol_file, stats_file):
    statistics = stat_base.copy()
    try:
        driver = minizinc.default_driver
        if config.minizinc is not None:
            driver = minizinc.CLI.CLIDriver(config.minizinc)
        instance = minizinc.Instance(config.solver, minizinc.Model(model),
                                     driver)
        if data is not None:
            instance.add_file(data, parse_data=False)
        is_satisfaction = instance.method == minizinc.Method.SATISFY

        for key, value in config.extra_data.items():
            instance[key] = value

        start = time.perf_counter()
        with sol_file.open(mode="w") as file:
            async for result in instance.solutions(
                    timeout=timeout,
                    processes=config.processes,
                    random_seed=config.random_seed,
                    intermediate_solutions=True,
                    free_search=config.free_search,
                    optimisation_level=config.optimisation_level,
                    **config.other_flags,
            ):
                solution = stat_base.copy()
                solution["status"] = str(result.status)
                if "time" in result.statistics:
                    solution["time"] = result.statistics.pop(
                        "time").total_seconds()
                if result.solution is not None:
                    solution["solution"] = asdict(result.solution)
                    solution["solution"].pop("_output_item", None)
                    solution["solution"].pop("_checker", None)
                file.write(ruamel.yaml.dump([solution]))

                statistics.update(result.statistics)
                statistics["status"] = str(result.status)
                if result.solution is not None and not is_satisfaction:
                    statistics["objective"] = result.solution.objective

        total_time = time.perf_counter() - start
        statistics["time"] = total_time
    except minizinc.MiniZincError as err:
        statistics["status"] = str(minizinc.result.Status.ERROR)
        statistics["error"] = str(err)

    for key, val in statistics.items():
        if isinstance(val, timedelta):
            statistics[key] = val.total_seconds()
    ruamel.yaml.dump(
        statistics,
        stats_file.open(mode="w"),
        default_flow_style=False,
    )
Ejemplo n.º 3
0
 def minizinc_model():
     src = "float: a;\n" \
           "var float: x;\n" \
           "constraint x == a + 1;\n" \
           "solve satisfy"
     model = minizinc.Model()
     model.add_string(src)
     solver = minizinc.Solver.lookup("cbc")
     inst = minizinc.Instance(solver, model)
     inst["a"] = 2
     result = inst.solve()
     return result
Ejemplo n.º 4
0
 def minizinc_model():
     src = "int: a;\n" \
           "int: b;\n" \
           "var -100..100: x;\n" \
           "constraint a < x;\n" \
           "constraint x < b;\n" \
           "solve satisfy"
     model = minizinc.Model()
     model.add_string(src)
     gecode = minizinc.Solver.lookup("gecode")
     inst = minizinc.Instance(gecode, model)
     inst["a"] = 1
     inst["b"] = 4
     result = inst.solve(all_solutions=True)
     return result
Ejemplo n.º 5
0
    def __init__(self, nombre_moutons):

        # Create a MiniZinc model
        self.model = mnz.Model()
        self.model.add_file("ProjSauteMouton.mzn")

        # Transform Model into a instance
        gecode = mnz.Solver.lookup("gecode")
        self.inst = mnz.Instance(gecode, self.model)

        self.nb_moutons = nombre_moutons
        self.inst["nbr_m"] = nombre_moutons

        self.moutons = []
        '''
Ejemplo n.º 6
0
 def solver_exists(self, solver):
     solver_exists = pytest.solver_cache.get(solver, None)
     if solver_exists is None:
         try:
             s = mzn.Solver.lookup(solver)
             empty_model = mzn.Model()
             empty_model.add_string("solve satisfy;")
             instance = mzn.Instance(s, empty_model)
             instance.solve()
             solver_exists = True
         except (mzn.MiniZincError, LookupError) as error:
             solver_exists = False
         finally:
             pytest.solver_cache[solver] = solver_exists
     return solver_exists
Ejemplo n.º 7
0
    def run(self, mzn_file, solver, default_options={}):
        """
        Runs this test case given an mzn file path, a solver name and some default options.

        Any options specified in this test case directly will override those provided in
        default options.

        Returns a tuple containing:
        - the model produced
        - the result of running the solver
        - a list of expected results
        - the actual obtained result

        Pass the actual obtained result to passed() to determine if the test case passed.
        """
        options = {k: v for k, v in default_options.items()}
        options.update(self.options)
        file = pathlib.Path(mzn_file)
        extra_files = [
            file.parent.joinpath(other) for other in self.extra_files
        ]

        try:
            model = mzn.Model([file] + extra_files)
            solver = mzn.Solver.lookup(solver)
            instance = mzn.Instance(solver, model)
            if self.type == "solve":
                instance.output_type = Solution
                result = instance.solve(**options)
                obtained = Result.from_mzn(result)
            elif self.type == "compile":
                with instance.flat(**options) as (fzn, ozn, stats):
                    obtained = FlatZinc.from_mzn(fzn, file.parent)
                    result = obtained
            elif self.type == "output-model":
                with instance.flat(**options) as (fzn, ozn, stats):
                    obtained = OutputModel.from_mzn(ozn, file.parent)
                    result = obtained
            else:
                raise NotImplementedError("Unknown test case type")
        except mzn.MiniZincError as error:
            result = error
            obtained = Error.from_mzn(result)

        required = self.expected if isinstance(self.expected,
                                               list) else [self.expected]

        return model, result, required, obtained
Ejemplo n.º 8
0
 def minizinc_model():
     src = "int: a;\n" \
           "int: b;\n" \
           "int: c;\n" \
           "var -100..100: x;\n" \
           "constraint ((((a * pow(x, 2)) + (b * x)) + c) = 0);\n" \
           "solve satisfy"
     model = minizinc.Model()
     model.add_string(src)
     gecode = minizinc.Solver.lookup("gecode")
     inst = minizinc.Instance(gecode, model)
     inst["a"] = 1
     inst["b"] = 4
     inst["c"] = 0
     result = inst.solve(all_solutions=True)
     return result
Ejemplo n.º 9
0
 def _solve(self, *how_to_solve, all_solutions, result_as, verbose, solver):
     solver = minizinc.Solver.lookup(solver)
     model = minizinc.Model()
     src = self.compile(how_to_solve)
     if verbose:
         print(src)
     model.add_string(src)
     inst = minizinc.Instance(solver, model)
     for name, param in self._ir.pars.items():
         inst[name] = param.value
     for e in self._ir.enums:
         inst[e.__name__] = e
     result: minizinc.Result = inst.solve(all_solutions=all_solutions)
     if result_as is None:
         return Result(result)
     else:
         return result_as(result)
Ejemplo n.º 10
0
    def _model(self, model, solvername=None):
        import minizinc
        if solvername is None:
            # default solver
            solvername = "gecode"

        # from superclass
        mzn_txt = self.convert(model)

        # minizinc-python API
        # Create a MiniZinc model
        mznmodel = minizinc.Model()
        mznmodel.add_string(mzn_txt)

        # Transform Model into a instance
        slv = minizinc.Solver.lookup(solvername)
        return minizinc.Instance(slv, mznmodel)
Ejemplo n.º 11
0
def schedule_evs(num_days,
                 num_periods_day,
                 peak_periods,
                 off_peak_periods,
                 num_evs,
                 max_charge,
                 total_energy,
                 prices_2d,
                 loads_2d,
                 network_tariff_peak,
                 network_tariff_off_peak,
                 model_file="scripts/ev-scheduling.mzn"):

    off_peak_periods2 = {i + 1 for i in off_peak_periods}.copy()
    if network_tariff_peak == network_tariff_off_peak:
        peak_periods2 = {i + 1 for i in range(num_periods_day)}.copy()
        network_tariff_off_peak = 0
    else:
        # minizinc index starts from 1
        peak_periods2 = {i + 1 for i in peak_periods}.copy()

    # build a MiniZinc model
    model = mzn.Model(model_file)
    solver = mzn.Solver.lookup("mip")
    ins = mzn.Instance(solver, model)
    ins["num_days"] = num_days
    ins["num_periods_day"] = num_periods_day
    ins["PEAK_PERIODS"] = peak_periods2
    ins["OFF_PEAK_PERIODS"] = off_peak_periods2
    ins["num_evs"] = num_evs
    ins["max_charge"] = max_charge
    ins["total_energy"] = total_energy
    ins["wholesale_prices"] = prices_2d
    ins["network_tariff_peak"] = network_tariff_peak
    ins["network_tariff_off_peak"] = network_tariff_off_peak
    ins["existing_loads"] = loads_2d
    result = ins.solve()

    # organise results
    charge_ev_day_period = result.solution.charge_strategy
    minizinc_outputs = ast.literal_eval(result.solution._output_item)
    minizinc_outputs["time (ms)"] = [
        result.statistics['solveTime'].microseconds * 0.001
    ]
    return charge_ev_day_period, minizinc_outputs
Ejemplo n.º 12
0
    def calcular(self, siCondicionesA):
        self.crearVarRegiones()
        self.kitsPorUnidad()
        self.Unidades()
        self.costosAdecuacion()
        
        if(siCondicionesA):
            self.condicionesAdicionales() 
        
        self.poblacion()
        self.noNegatividad()
        self.funcionObjetivo()
        
        # desde aca se resilve el codigo en minizinc, en alguna parte desde aca iria la funcion de parar en X minutos        
        
        # Create a MiniZinc model
        model = mz.Model()
        model.add_string(self.stringFinal)

        # Transform Model into a instance
        gecode = mz.Solver.lookup("gecode")
        inst = mz.Instance(gecode, model)

        # Solve the instance
        result = inst.solve()
        
        print("*************************   RESULTADOS MINIZINC   *************************")
        
        for regionX in self.listaDeRegiones:
            nombreRegionX = str(regionX.get_nombreCorto()) + " = "
            resultadoX = result[str(regionX.get_nombreCorto())]
            print(resultadoX)
            
            print(nombreRegionX, end = "")
            print(resultadoX)
            
            self.stringResultado = self.stringResultado + nombreRegionX + str(resultadoX) + "\n"

        self.stringResultado = self.stringResultado + "Beneficio = " + str(result["Beneficio"]) + "\n"
        
        print("Beneficio", end = " = ")
        print(result["Beneficio"])
        print("\n*************************   FIN DE PROGRAMA   *************************")
Ejemplo n.º 13
0
def schedule_evs(num_days, prices_2d, loads_2d, network_tariff_peak,
                 network_tariff_off_peak):

    # build a MiniZinc model
    model = mzn.Model(model_file)
    solver = mzn.Solver.lookup("coin-bc")
    ins = mzn.Instance(solver, model)
    ins["num_days"] = num_days
    ins["num_periods_day"] = num_periods_day
    ins["num_evs"] = num_evs
    ins["max_charge"] = max_charge
    ins["total_energy"] = total_energy
    ins["wholesale_prices"] = prices_2d
    ins["network_tariff_peak"] = network_tariff_peak
    ins["network_tariff_off_peak"] = network_tariff_off_peak
    ins["existing_loads"] = loads_2d
    result = ins.solve()

    return result
Ejemplo n.º 14
0
    def from_feature_model(
        cls,
        feature_model: FeatureModel,
        solver: minizinc.Solver = minizinc.Solver.lookup("gecode"),
    ) -> MZFMSolver:
        mini_zinc_vars = ""
        mini_zinc_const = ""

        optional_features = []
        for feature in feature_model.features:
            mini_zinc_vars = "".join([mini_zinc_vars, gen_variable(feature)])
            if feature.constraints:
                for constraint in feature.constraints:
                    mini_zinc_const = "".join(
                        [
                            mini_zinc_const,
                            gen_constraint(constraint, feature.id),
                        ]
                    )

                    if type(constraint) == Optional:
                        optional_features.append(f"feature_{constraint.destination}")

        feature_names = [f"feature_{feature.id}" for feature in feature_model.features]

        mini_zinc_str = "\n".join([mini_zinc_vars, mini_zinc_const, "solve satisfy;"])

        model = minizinc.Model()
        model.add_string(mini_zinc_str)

        return cls(
            model=model,
            feature_names=feature_names,
            optional_features=optional_features,
            solver=solver,
        )
Ejemplo n.º 15
0
 def __init__(self, params={}):
     self.model = mz.Model()
     self.params = params
     self.frozen = False
Ejemplo n.º 16
0
                    for a in range(len(positions)):
                        if positions[a][i] < positions[a][j]:
                            agent_prefs += 1
                        else:
                            agent_prefs -= 1
                    if agent_prefs > 0:
                        n_wins += 1
            copeland.append(n_wins)
        votes = [(w, solutions[i]) for i, w in enumerate(copeland)]
        return sorted(votes, key=lambda v: v[0], reverse=True)


if __name__ == "__main__":

    args = argparser.parse_args()
    m = minizinc.Model()
    m.add_file(args.model)
    m.add_file(args.data)
    agents = [Agent(a, m) for a in range(len(m["Passenger"]))]

    n_steps = args.iterations
    n_agents = len(agents)
    n_sol_per_agent = args.sol_per_agent

    d = Diversifier(m)
    s = d.next_k(n_agents * n_sol_per_agent, [])

    voting = None
    if args.voting_method == "borda":
        voting = Borda()
    elif args.voting_method == "copeland":
Ejemplo n.º 17
0
def csp(blocks, sheets, matches):
    print('numblocks', len(blocks))
    blocks = blocks[:]
    model = minizinc.Model()
    model.add_string("""
    include "globals.mzn";

    int: num_blocks;
    int: num_rows;
    int: num_cols;
    set of int: b=1..num_blocks;
    set of int: rows=1..num_rows;
    set of int: cols=1..num_cols;
    array[b] of cols: x1;
    array[b] of cols: x2;
    array[b] of rows: y1;
    array[b] of rows: y2;
    array[b] of int: w;
    array[b] of int: h;
    array[rows] of var int: rowsa;
    array[cols] of var int: colsa;
    int: num_left;
    int: num_above;
    set of int: l=1..num_left;
    set of int: t=1..num_above;
    array[l] of cols: left1;
    array[l] of cols: left2;
    array[t] of rows: above1;
    array[t] of rows: above2;
    
    int: num_left_equal;
    int: num_above_equal;
    set of int: le=1..num_left_equal;
    set of int: te=1..num_above_equal;
    array[le] of cols: left_equal1;
    array[le] of cols: left_equal2;
    array[te] of rows: above_equal1;
    array[te] of rows: above_equal2;
    
    int: max_col = sum(w) + 1;
    int: max_row = sum(h) + 1;
        
    array[b] of var bool: use_block;
    array[b] of var int: wf;
    array[b] of var int: hf;
    constraint forall(bl in b) ((use_block[bl] -> wf[bl] = w[bl]) /\ 
                                (not use_block[bl] -> wf[bl] = 0));
    constraint forall(bl in b) ((use_block[bl] -> hf[bl] = h[bl]) /\ 
                                (not use_block[bl] -> hf[bl] = 0));
                                
    array[l] of var bool: use_left;
    array[t] of var bool: use_above;   
    array[le] of var bool: use_left_equal;                         
    array[te] of var bool: use_above_equal;                         

    constraint forall(c in cols) (colsa[c] > 0 /\ colsa[c] <= max_col);
    constraint forall(r in rows) (rowsa[r] > 0 /\ rowsa[r] <= max_row);

    constraint diffn([colsa[x1[bl]] | bl in b], 
                     [rowsa[y1[bl]] | bl in b],  
                     [wf[bl] | bl in b],
                     [hf[bl] | bl in b]);
    constraint forall(i in b) (colsa[x1[i]] + w[i] -1= colsa[x2[i]]);
    constraint forall(i in b) (rowsa[y1[i]] + h[i] -1= rowsa[y2[i]]);
    constraint forall(i in l) (use_left[i] <-> (colsa[left1[i]] < colsa[left2[i]]));
    constraint forall(i in t) (use_above[i] <-> (rowsa[above1[i]] < rowsa[above2[i]]));
    constraint forall(i in le) (use_left_equal[i] <-> (colsa[left_equal1[i]] = colsa[left_equal2[i]]));
    constraint forall(i in te) (use_above_equal[i] <-> (rowsa[above_equal1[i]] = rowsa[above_equal2[i]]));

    %solve satisfy;
    %solve minimize sum(rowsa) + sum(colsa);
    solve maximize sum(use_left) + sum(use_above) + sum(use_block) + sum(use_left_equal) + sum(use_above_equal);    
    """)

    cols = sorted(
        list(
            set(block.x1
                for block in blocks).union(set(block.x2 for block in blocks))))
    rows = sorted(
        list(
            set(block.y1
                for block in blocks).union(set(block.y2 for block in blocks))))
    relative_left, relative_left_equals = _parse_indices(cols, matches)
    relative_above, relative_above_equals = _parse_indices(rows, matches)
    colsi = {col: i + 1 for i, col in enumerate(cols)}
    rowsi = {row: i + 1 for i, row in enumerate(rows)}
    left, above = _location_constraints(cols, rows, sheets, matches)
    left = [(l1, l2)
            for l1, l2 in left if l1 in cols and l2 in cols] + relative_left
    above = [(a1, a2)
             for a1, a2 in above if a1 in rows and a2 in rows] + relative_above
    print('blocks', blocks)

    ProjectedBlock = collections.namedtuple('ProjectedBlock',
                                            'x1 y1 x2 y2 width height')
    pblocks = list(
        set(
            ProjectedBlock(block.x1, block.y1, block.x2, block.y2, block.width,
                           block.height) for block in blocks))
    for b in blocks:
        print(' ', b)
    print(len(blocks), len(pblocks))
    for b in pblocks:
        print('  ', b)
    print('pblocks', len(pblocks), pblocks)
    print('colsi', colsi)
    print('rowsi', rowsi)

    gecode = minizinc.Solver.lookup("chuffed")
    minst = minizinc.Instance(gecode, model)
    inst = {}
    inst['num_blocks'] = len(pblocks)
    inst['num_rows'] = len(rows)
    inst['num_cols'] = len(cols)
    inst['x1'] = [colsi[block.x1] for block in pblocks]
    inst['x2'] = [colsi[block.x2] for block in pblocks]
    inst['y1'] = [rowsi[block.y1] for block in pblocks]
    inst['y2'] = [rowsi[block.y2] for block in pblocks]
    inst['w'] = [block.width for block in pblocks]
    inst['h'] = [block.height for block in pblocks]
    inst['num_left'] = len(left)
    inst['num_above'] = len(above)
    inst['left1'] = [colsi[l1] for l1, _ in left]
    inst['left2'] = [colsi[l2] for _, l2 in left]
    inst['above1'] = [rowsi[a1] for a1, _ in above]
    inst['above2'] = [rowsi[a2] for _, a2 in above]
    inst['num_left_equal'] = len(relative_left_equals)
    inst['num_above_equal'] = len(relative_above_equals)
    inst['left_equal1'] = [colsi[l1] for l1, _ in relative_left_equals]
    inst['left_equal2'] = [colsi[l2] for _, l2 in relative_left_equals]
    inst['above_equal1'] = [rowsi[a1] for a1, _ in relative_above_equals]
    inst['above_equal2'] = [rowsi[a2] for _, a2 in relative_above_equals]

    print(inst)
    for k, v in inst.items():
        minst[k] = v
    # Solve the instance
    print('CSP')
    result = minst.solve(all_solutions=False)
    print('res', result)
    print(result['rowsa'])
    print(result['colsa'])
    assignment = {col: r for col, r in zip(cols, result['colsa'])}
    assignment.update(**{row: r for row, r in zip(rows, result['rowsa'])})
    used_blocks = {}
    for block, used in zip(pblocks, result['use_block']):
        used_blocks[block] = used
        print(used)
        if not used:
            print('Warning not used:', block)
            for oblock in blocks:
                if oblock.x1 == block.x1 and oblock.y1 == block.y1 and oblock.x2 == block.x2 and oblock.y2 == block.y2:
                    print('Original block:', oblock)

    print('assignment', assignment)
    return assignment
Ejemplo n.º 18
0
def run_minizinc_model(params):
    mdl_name, instance_name, timeout = params

    instance_path = os.path.join(INSTANCE_FOLDER, instance_name)

    if not os.path.isfile(mdl_name):
        raise Exception("ERROR: mdl_name not found: ", mdl_name)

    if not os.path.isfile(instance_path):
        raise Exception("ERROR: instance_name not found: ", instance_name)

    print("*** Processing model {} with instance {}".format(mdl_name, instance_name))


    model = minizinc.Model([mdl_name])

    instance = minizinc.Instance(SOLVER, model)


    with open(instance_path, 'r') as f:
        input_data = json.load(f)
        scenesLength, actorsToScenes, actorsCosts = input_data['scenesLength'],input_data['actorsToScenes'], input_data['actorsCosts']


    num_scenes = len(scenesLength)
    num_actors = len(actorsCosts)
    if "1" in mdl_name:

        actorsToScenes_new = np.zeros((len(actorsCosts), len(scenesLength)))
        for actor, scenes in enumerate(actorsToScenes):
            actorsToScenes_new[actor, scenes] = 1


        data = {
            "num_scenes": len(scenesLength),
            "num_actors": len(actorsCosts),
            "max_cost": max(actorsCosts),
            "duration": scenesLength,
            "actor_cost": actorsCosts,
            "actorsToScenes": actorsToScenes_new.astype(int).tolist()
            }
    else:
        actorsToScenes_new = [set([i + 1 for i in scenes]) for scenes in actorsToScenes]
        data = {
            "num_scenes": len(scenesLength),
            "num_actors": len(actorsCosts),
            "max_cost": max(actorsCosts),
            "max_SceneDuration": max(scenesLength),
            "max_ActorCost":max(actorsCosts),
            "duration": scenesLength,
            "cost": actorsCosts,
            "actorsToScenes": actorsToScenes_new
            }


    for key, val in data.items():
        instance[key] = val

    msol = instance.solve(verbose=True, timeout=datetime.timedelta(seconds=timeout))


    # output= {
    #
    # }
    if msol:
        results = [
            msol.status.name,
            msol.solution.objective,
            msol.statistics['initTime'].total_seconds(),
            msol.statistics['solveTime'].total_seconds(),
            msol.statistics['variables']]
        if "1" in mdl_name:
            scene_order = msol['scene_order']
        else:
            scene_order = msol['order']
        success = True


    else:
        results = [msol.status.name if msol else "NOTFOUND"]
        success = False
        scene_order = None

    return success, mdl_name, instance_name, timeout, num_actors, num_scenes, scene_order, results
Ejemplo n.º 19
0
def partial_schedule(volunteers, vehicle_request):
    # create an array of preferredHours
    preferred_hours = []
    for volunteer in volunteers:
        preferred_hours.append(volunteer["prefHours"])

    # generates a list of shift lengths corresponding to each vehicle request
    shift_lengths = generate_shift_lengths(vehicle_request)

    # generates a 2d array of booleans for the model
    # each entry denotes the compatability of 1 volunteer to 1 request
    compatible = generate_compatibility(volunteers, vehicle_request)

    # generates a 2d array of booleans for the model
    # each entry denotes whether there is a clash (true) between the corresponding vehicle requests
    clashing = generate_clashes(vehicle_request)

    # the following codeblock generates 3 lists of booleans
    # if is_heavy(index) = true then vehicle_request(index) is a heavy tanker
    # if is_medium(index) = true then vehicle_request(index) is a medium tanker
    # if is_light(index) = true then vehicle_request(index) is a light unit
    is_heavy = []
    is_medium = []
    is_light = []
    for request in vehicle_request:
        if request["assetClass"] == "heavyTanker":
            is_heavy.append(True)
            is_medium.append(False)
            is_light.append(False)
        if request["assetClass"] == "mediumTanker":
            is_heavy.append(False)
            is_medium.append(True)
            is_light.append(False)
        if request["assetClass"] == "lightUnit":
            is_heavy.append(False)
            is_medium.append(False)
            is_light.append(True)

    # the following codeblock generates 4 lists of booleans
    # if is_driver(index) = true then volunteer(index) is a driver
    # if is_crew_leader(index) = true then volunteer(index) is a crewleader
    # if is_advanced(index) = true then volunteer(index) is advanced
    # if is_basic(index) = true then volunteer(index) is basic
    is_driver = []
    is_crew_leader = []
    is_advanced = []
    is_basic = []
    for volunteer in volunteers:
        basic = False
        advanced = False
        crewLeader = False
        driver = False
        for qualification in volunteer["possibleRoles"]:
            if qualification == "basic":
                basic = True
            if qualification == "advanced":
                advanced = True
            if qualification == "crewLeader":
                crewLeader = True
            if qualification == "driver":
                driver = True
        is_basic.append(basic)
        is_advanced.append(advanced)
        is_crew_leader.append(crewLeader)
        is_driver.append(driver)

    # we are using the gecode optimiser
    gecode = minizinc.Solver.lookup("gecode")
    # create the model
    model = minizinc.Model()
    # the following string generates the minizinc model for a full solve
    model.add_string("""
        int: V;
        int: S;
        set of int: volunteers = 1..V;
        set of int: Shifts = 1..S;

        array[Shifts,volunteers] of bool: compatible;
        array[Shifts,Shifts] of bool: clashing;
        array[Shifts,volunteers] of var bool: assignments;
        array[volunteers] of int: preferredHours;
        array[Shifts] of int: shiftLength;

        array[Shifts] of bool: is_heavy;
        array[Shifts] of bool: is_medium;
        array[Shifts] of bool: is_light;

        array[volunteers] of bool: is_basic;
        array[volunteers] of bool: is_advanced;
        array[volunteers] of bool: is_crew_leader;
        array[volunteers] of bool: is_driver;

        array[Shifts] of var 0..V: seat1;
        array[Shifts] of var 0..V: seat2;
        array[Shifts] of var 0..V: seat3;
        array[Shifts] of var 0..V: seat4;
        array[Shifts] of var 0..V: seat5;

        array[volunteers,Shifts] of var int: hoursOnAssignment;
        array[volunteers] of var int: totalHours;

        %volunteer availability check
        constraint forall(s in Shifts)(forall(v in volunteers)(if compatible[s,v] == false then assignments[s,v] == false endif));

        %volunteers cannot be assigned to 2 shifts at once
        constraint forall(s1 in Shifts)(forall(s2 in Shifts)(forall(v in volunteers)(if clashing[s1,s2] /\ assignments[s1,v] then assignments[s2,v] == false endif)));

        %all vehicles are (at most) filled. This allows for partial fills
        constraint forall(s in Shifts)(if is_heavy[s] then sum(v in volunteers)(assignments[s,v]) <= 5 endif);
        constraint forall(s in Shifts)(if is_medium[s] then sum(v in volunteers)(assignments[s,v]) <= 3 endif);
        constraint forall(s in Shifts)(if is_light[s] then sum(v in volunteers)(assignments[s,v]) <= 2 endif);

        %Seat1 Requirements
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat1[s] == v then assignments[s,v] = true endif));
        constraint forall(s in Shifts)(forall(v in volunteers)(if seat1[s] == v then is_driver[v] = true endif));
        constraint forall(s in Shifts)(if is_light[s] then forall(v in volunteers)(if seat1[s] == v then is_crew_leader[v] = true endif) endif);

        %seat2 Requirements
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat2[s] == v then assignments[s,v] = true endif));
        constraint forall(s in Shifts)(if is_light[s] then forall(v in volunteers)(if seat2[s] == v then is_advanced[v] = true endif) endif);
        constraint forall(s in Shifts)(if is_medium[s] then forall(v in volunteers)(if seat2[s] == v then is_crew_leader[v] = true endif) endif);
        constraint forall(s in Shifts)(if is_heavy[s] then forall(v in volunteers)(if seat2[s] == v then is_crew_leader[v] = true endif) endif);

        %seat3 Requirements
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat3[s] == v then assignments[s,v] = true endif));
        constraint forall(s in Shifts)(if is_light[s] then seat3[s] == seat2[s] endif);
        constraint forall(s in Shifts)(if is_medium[s] then forall(v in volunteers)(if seat3[s] == v then is_basic[v] = true endif) endif);
        constraint forall(s in Shifts)(if is_heavy[s] then forall(v in volunteers)(if seat3[s] == v then is_advanced[v] = true endif) endif);

        %seat4 Requirements
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat4[s] == v then assignments[s,v] = true endif));
        constraint forall(s in Shifts)(if is_light[s] then seat4[s] == seat2[s] endif);
        constraint forall(s in Shifts)(if is_medium[s] then seat4[s] == seat3[s] endif);
        constraint forall(s in Shifts)(if is_heavy[s] then forall(v in volunteers)(if seat4[s] == v then is_advanced[v] = true endif) endif);

        %seat5 Requirements
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat5[s] == v then assignments[s,v] = true endif));
        constraint forall(s in Shifts)(if is_light[s] then seat5[s] == seat2[s] endif);
        constraint forall(s in Shifts)(if is_medium[s] then seat5[s] == seat3[s] endif);
        constraint forall(s in Shifts)(if is_heavy[s] then forall(v in volunteers)(if seat5[s] == v then is_basic[v] = true endif) endif);

        %if a volunteer is not on any seats of a shift, they are not on the shift
        constraint forall(s in Shifts)(forall(v in volunteers) (if seat5[s] != v /\ seat4[s] != v /\ seat3[s] != v /\ seat2[s] != v /\ seat1[s] != v then assignments[s,v] = false endif));


        %maps shifts to hours worked
        constraint forall(v in volunteers)(forall(s in Shifts)(if assignments[s,v] then hoursOnAssignment[v,s] = shiftLength[s] else hoursOnAssignment[v,s] = 0 endif));

        %defines total hours for each volunteer
        constraint forall(v in volunteers)(totalHours[v] == sum(s in Shifts)(hoursOnAssignment[v,s]));

        %ensures no one is overworked
        constraint forall(v in volunteers)(totalHours[v] <= preferredHours[v]);

        %maximise for number of volunteers assigned
        solve maximize sum(s in Shifts)(sum(v in volunteers)(assignments[s,v]))
        """)
    # initialise all the variables in the minizinc model from the variables we previously created
    instance = minizinc.Instance(gecode, model)
    instance["V"] = len(volunteers)
    instance["S"] = len(vehicle_request)
    instance["preferredHours"] = preferred_hours
    instance["shiftLength"] = shift_lengths
    instance["compatible"] = compatible
    instance["clashing"] = clashing
    instance["is_heavy"] = is_heavy
    instance["is_medium"] = is_medium
    instance["is_light"] = is_light
    instance["is_basic"] = is_basic
    instance["is_advanced"] = is_advanced
    instance["is_crew_leader"] = is_crew_leader
    instance["is_driver"] = is_driver
    # solve the model
    result = instance.solve()
    # if there is a valid solution, format it and output
    if result.solution:
        output = []
        for i in range(len(vehicle_request)):
            vehicle_dict = {}
            vehicle_dict["shiftID"] = vehicle_request[i]["shiftID"]
            vehicle_dict["assetClass"] = vehicle_request[i]["assetClass"]
            vehicle_dict["timeframe"] = vehicle_request[i]["timeframe"]
            seats = []
            seats.append(result["seat1"])
            seats.append(result["seat2"])
            seats.append(result["seat3"])
            seats.append(result["seat4"])
            seats.append(result["seat5"])
            if vehicle_dict["assetClass"] == "lightUnit":
                volunteers = add_light_unit_to_output(seats, i, volunteers)
            if vehicle_dict["assetClass"] == "mediumTanker":
                volunteers = add_medium_tanker_to_output(seats, i, volunteers)
            if vehicle_dict["assetClass"] == "heavyTanker":
                volunteers = add_heavy_tanker_to_output(seats, i, volunteers)
            vehicle_dict["volunteers"] = volunteers
            output.append(vehicle_dict)
    # if there is no valid model, output an empty list
    else:
        print("No partial fill possible")
        output = []
    return output