2020from  sampo .schemas .landscape  import  LandscapeConfiguration 
2121from  sampo .schemas .resources  import  Worker 
2222from  sampo .schemas .schedule  import  Schedule 
23- from  sampo .schemas .schedule_spec  import  ScheduleSpec 
23+ from  sampo .schemas .schedule_spec  import  ScheduleSpec ,  WorkSpec 
2424from  sampo .schemas .time  import  Time 
2525from  sampo .schemas .time_estimator  import  WorkTimeEstimator , DefaultWorkEstimator 
2626from  sampo .utilities .resource_usage  import  resources_peaks_sum , resources_costs_sum , resources_sum 
@@ -165,6 +165,7 @@ def init_toolbox(wg: WorkGraph,
165165                 parents : dict [int , set [int ]],
166166                 children : dict [int , set [int ]],
167167                 resources_border : np .ndarray ,
168+                  contractors_available : np .ndarray ,
168169                 assigned_parent_time : Time  =  Time (0 ),
169170                 fitness_weights : tuple [int  |  float , ...] =  (- 1 ,),
170171                 work_estimator : WorkTimeEstimator  =  DefaultWorkEstimator (),
@@ -200,7 +201,8 @@ def init_toolbox(wg: WorkGraph,
200201    # combined mutation 
201202    toolbox .register ('mutate' , mutate , order_mutpb = mut_order_pb , res_mutpb = mut_res_pb , zone_mutpb = mut_zone_pb ,
202203                     rand = rand , parents = parents , children = children , resources_border = resources_border ,
203-                      statuses_available = statuses_available , priorities = priorities )
204+                      contractors_available = contractors_available , statuses_available = statuses_available ,
205+                      priorities = priorities )
204206    # crossover for order 
205207    toolbox .register ('mate_order' , mate_scheduling_order , rand = rand , toolbox = toolbox , priorities = priorities )
206208    # mutation for order 
@@ -210,7 +212,7 @@ def init_toolbox(wg: WorkGraph,
210212    toolbox .register ('mate_resources' , mate_resources , rand = rand , toolbox = toolbox )
211213    # mutation for resources 
212214    toolbox .register ('mutate_resources' , mutate_resources , resources_border = resources_border ,
213-                      mutpb = mut_res_pb , rand = rand )
215+                      contractors_available = contractors_available ,  mutpb = mut_res_pb , rand = rand )
214216    # mutation for resource borders 
215217    toolbox .register ('mutate_resource_borders' , mutate_resource_borders , contractor_borders = contractor_borders ,
216218                     mutpb = mut_res_pb , rand = rand )
@@ -219,7 +221,7 @@ def init_toolbox(wg: WorkGraph,
219221                     statuses_available = landscape .zone_config .statuses .statuses_available ())
220222
221223    toolbox .register ('validate' , is_chromosome_correct , node_indices = node_indices , parents = parents ,
222-                      contractor_borders = contractor_borders , index2node = index2node )
224+                      contractor_borders = contractor_borders , index2node = index2node ,  index2contractor = index2contractor_obj )
223225    toolbox .register ('schedule_to_chromosome' , convert_schedule_to_chromosome ,
224226                     work_id2index = work_id2index , worker_name2index = worker_name2index ,
225227                     contractor2index = contractor2index , contractor_borders = contractor_borders , spec = spec ,
@@ -324,9 +326,6 @@ def randomized_init(is_topological: bool = False) -> ChromosomeType:
324326            case  _:
325327                ind  =  init_chromosomes [generated_type ][0 ]
326328
327-         if  not  toolbox .validate (ind ):
328-             SAMPO .logger .warn ('HELP' )
329- 
330329        ind  =  toolbox .Individual (ind )
331330        chromosomes .append (ind )
332331
@@ -390,12 +389,13 @@ def select_new_population(population: list[Individual], k: int) -> list[Individu
390389
391390
392391def  is_chromosome_correct (ind : Individual , node_indices : list [int ], parents : dict [int , set [int ]],
393-                           contractor_borders : np .ndarray , index2node : dict [int , GraphNode ]) ->  bool :
392+                           contractor_borders : np .ndarray , index2node : dict [int , GraphNode ],
393+                           index2contractor : dict [int , Contractor ]) ->  bool :
394394    """ 
395395    Check correctness of works order and contractors borders. 
396396    """ 
397397    return  is_chromosome_order_correct (ind , parents , index2node ) and  \
398-         is_chromosome_contractors_correct (ind , node_indices , contractor_borders )
398+         is_chromosome_contractors_correct (ind , node_indices , contractor_borders ,  index2node ,  index2contractor )
399399
400400
401401def  is_chromosome_order_correct (ind : Individual , parents : dict [int , set [int ]], index2node : dict [int , GraphNode ]) ->  bool :
@@ -419,13 +419,27 @@ def is_chromosome_order_correct(ind: Individual, parents: dict[int, set[int]], i
419419
420420
421421def  is_chromosome_contractors_correct (ind : Individual , work_indices : Iterable [int ],
422-                                       contractor_borders : np .ndarray ) ->  bool :
422+                                       contractor_borders : np .ndarray ,
423+                                       index2node : dict [int , GraphNode ],
424+                                       index2contractor : dict [int , Contractor ]) ->  bool :
423425    """ 
424426    Checks that assigned contractors can supply assigned workers. 
425427    """ 
426428    if  not  work_indices :
427429        return  True 
430+ 
431+     order  =  ind [0 ]
428432    resources  =  ind [1 ][work_indices ]
433+ 
434+     # check contractor align with the spec 
435+     spec : ScheduleSpec  =  ind [3 ]
436+     contractors  =  resources [:, - 1 ]
437+     for  i  in  range (len (order )):
438+         work_index  =  order [i ]
439+         work_spec  =  spec [index2node [work_index ].id ]
440+         if  not  work_spec .is_contractor_enabled (index2contractor [contractors [work_index ]].id ):
441+             return  False 
442+ 
429443    # sort resource part of chromosome by contractor ids 
430444    resources  =  resources [resources [:, - 1 ].argsort ()]
431445    # get unique contractors and indexes where they start 
@@ -579,8 +593,8 @@ def mutate_scheduling_order(ind: Individual, mutpb: float, rand: random.Random,
579593    """ 
580594    order  =  ind [0 ]
581595
582-     priority_groups_count  =  len (set (priorities ))
583-     mutpb_for_priority_group  =  mutpb  # / priority_groups_count
596+     #  priority_groups_count = len(set(priorities))
597+     mutpb_for_priority_group  =  mutpb    #  / priority_groups_count
584598
585599    # priorities of tasks with same order-index should be the same (if chromosome is valid) 
586600    cur_priority  =  priorities [order [0 ]]
@@ -634,15 +648,17 @@ def mate_resources(ind1: Individual, ind2: Individual, rand: random.Random,
634648
635649
636650def  mutate_resources (ind : Individual , mutpb : float , rand : random .Random ,
637-                      resources_border : np .ndarray ) ->  Individual :
651+                      resources_border : np .ndarray ,
652+                      contractors_available : np .ndarray ) ->  Individual :
638653    """ 
639654    Mutation function for resources. 
640655    It changes selected numbers of workers in random work in a certain interval for this work. 
641656
642657    :param ind: the individual to be mutated 
643-     :param resources_border: low and up borders of resources amounts 
644658    :param mutpb: probability of gene mutation 
645659    :param rand: the rand object used for randomized operations 
660+     :param resources_border: low and up borders of resources amounts 
661+     :param contractors_available: mask of contractors available to do tasks 
646662
647663    :return: mutated individual 
648664    """ 
@@ -654,9 +670,22 @@ def mutate_resources(ind: Individual, mutpb: float, rand: random.Random,
654670        mask  =  np .array ([rand .random () <  mutpb  for  _  in  range (num_works )])
655671        if  mask .any ():
656672            # generate new contractors in the number of received True values of mask 
657-             new_contractors  =  np .array ([rand .randint (0 , num_contractors  -  1 ) for  _  in  range (mask .sum ())])
673+ 
674+             # [rand.randint(0, num_contractors - 1) for _ in range(mask.sum())] 
675+             new_contractors_list  =  []
676+ 
677+             # TODO Rewrite to numpy functions if heavy 
678+             for  task , task_selected  in  enumerate (mask ):
679+                 if  not  task_selected :
680+                     continue 
681+                 contractors_to_select  =  np .where (contractors_available [task ] ==  1 )
682+                 new_contractors_list .append (rand .choices (contractors_to_select [0 ], k = 1 )[0 ])
683+ 
684+             new_contractors  =  np .array (new_contractors_list )
685+ 
658686            # obtain a new mask of correspondence 
659687            # between the borders of the received contractors and the assigned resources 
688+ 
660689            contractor_mask  =  (res [mask , :- 1 ] <=  ind [2 ][new_contractors ]).all (axis = 1 )
661690            # update contractors by received mask 
662691            new_contractors  =  new_contractors [contractor_mask ]
@@ -720,9 +749,9 @@ def mate(ind1: Individual, ind2: Individual, optimize_resources: bool,
720749    return  toolbox .Individual (child1 ), toolbox .Individual (child2 )
721750
722751
723- def  mutate (ind : Individual , resources_border : np .ndarray , parents :  dict [ int ,  set [ int ]] ,
724-            children : dict [int , set [int ]], statuses_available :  int , priorities :  np . ndarray ,
725-            order_mutpb : float , res_mutpb : float , zone_mutpb : float ,
752+ def  mutate (ind : Individual , resources_border : np .ndarray , contractors_available :  np . ndarray ,
753+            parents : dict [int , set [int ]], children :  dict [ int , set [ int ]],  statuses_available :  int ,
754+            priorities :  np . ndarray ,  order_mutpb : float , res_mutpb : float , zone_mutpb : float ,
726755           rand : random .Random ) ->  Individual :
727756    """ 
728757    Combined mutation function of mutation for order, mutation for resources and mutation for zones. 
@@ -740,7 +769,7 @@ def mutate(ind: Individual, resources_border: np.ndarray, parents: dict[int, set
740769    :return: mutated individual 
741770    """ 
742771    mutant  =  mutate_scheduling_order (ind , order_mutpb , rand , priorities , parents , children )
743-     mutant  =  mutate_resources (mutant , res_mutpb , rand , resources_border )
772+     mutant  =  mutate_resources (mutant , res_mutpb , rand , resources_border ,  contractors_available )
744773    # TODO Make better mutation for zones and uncomment this 
745774    # mutant = mutate_for_zones(mutant, statuses_available, zone_mutpb, rand) 
746775
0 commit comments