optapy / optapy

OptaPy is an AI constraint solver for Python to optimize planning and scheduling problems.

Home Page:https://www.optapy.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A bug in the count() function

wemiam-co opened this issue · comments

commented

Hi,

I think there is a bug in the count function.

When i set this constraint :

def nbr_rdv_resp_mois(constraint_factory: ConstraintFactory):
    return (
        constraint_factory.for_each(Talent)
                                        .group_by(lambda talent: talent.resp.name_resp,
                                                  ConstraintCollectors.count())
                                        .filter(lambda name_resp, count: count > 2)
                                        .penalize('nbr_rdv_resp_mois',HardMediumSoftScore.ONE_HARD)

I don't get the expected result.

When the constraint is disabled : I get 3 lines with 3 different name_resp: A , B and C => correct
When i enable the constraint I get 2 lines with A and B. But i should get A, B, C.

This is too vague and does not contains enough details (in particular, I need an example planning solution/planning entities).

Your constraints, as written, reads: "Penalize by 1 hard for each name_resp that have more than 2 talents assigned to it". For example, given

resp_a = Resp('A')
resp_b = Resp('B')
resp_c = Resp('C')

talent_1 = Talent(resp_a)
talent_2 = Talent(resp_a)
talent_3 = Talent(resp_b)
talent_4 = Talent(resp_b)
talent_5 = Talent(resp_b)
talent_6 = Talent(resp_c)
talent_7 = Talent(resp_c)
talent_8 = Talent(resp_c)
talent_9 = Talent(resp_c)

The tuples produced by group_by would be ('A', 2), ('B', 3), and ('C', 4), of which only ('B', 3), and ('C', 4) would get pass the filter and be penalize. You can test constraints using ConstraintVerifier; see https://github.com/optapy/optapy-quickstarts/blob/stable/school-timetabling/tests.py for an example.

commented

Thanks for your help,
Yes that's whet i was expecting, but it doesn't work. when i set the liit to 2, in stead of having results with only one occurrence, i get no results.

Here are the classes :

class Resp:
id: int
name: str
date_dispo: datetime.date
creneau_dispo: str
last_rdv: datetime.date
collab: str
param_strat: str
def init(self, id, name_resp, date_dispo, creneau_dispo, last_rdv,collab,param_strat):
self.id = id
self.name_resp = name_resp
self.date_dispo=date_dispo
self.creneau_dispo=creneau_dispo
self.last_rdv=last_rdv
self.collab=collab
self.param_strat=param_strat

@planning_id
def get_id(self):
    return self.id

def __str__(self):
    return f"id={self.id}, name_resp={self.name_resp}, date_dispo={self.date_dispo}, creneau_dispo={self.creneau_dispo}, last_rdv={self.last_rdv}, collab={self.collab}, param_strat={self.param_strat}\n"
def __repr__(self):
    return self.__str__()
@planning_entity
class Talent:
id: int
name: str
dateslot: Dateslot
resp: Resp
talent_creneau: Talent_creneau
def init(self, id, name, dateslot=None, resp=None, talent_creneau=None):
self.id = id
self.name = name
self.dateslot = dateslot
self.resp = resp
self.talent_creneau=talent_creneau

@planning_id
def get_id(self):
    return self.id

@planning_variable(Dateslot, ["dateslotRange"],nullable = False)
def get_dateslot(self):
    return self.dateslot

def set_dateslot(self, new_dateslot):
    self.dateslot = new_dateslot

@planning_variable(Resp, ["respRange"],nullable = True)
def get_resp(self):
    return self.resp

def set_resp(self, new_resp):
    self.resp = new_resp

You are not giving enough details; the case I wrote works as expected:

resp_a = Resp(0, 'a', datetime.date(2000, 1, 1), 'b', datetime.date(2000, 2, 1),
             'c', 'd')
resp_b = Resp(1, 'b', datetime.date(2000, 1, 1), 'b', datetime.date(2000, 2, 1),
             'c', 'd')
resp_c = Resp(2, 'c', datetime.date(2000, 1, 1), 'b', datetime.date(2000, 2, 1),
             'c', 'd')

constraint_verifier.verify_that(my_constraint) \
    .given(Talent(0, 'Talent 1', Dateslot(), resp_a),
           Talent(1, 'Talent 2', Dateslot(), resp_a),
           Talent(2, 'Talent 3', Dateslot(), resp_b),
           Talent(3, 'Talent 4', Dateslot(), resp_b),
           Talent(4, 'Talent 5', Dateslot(), resp_b),
           Talent(5, 'Talent 6', Dateslot(), resp_c),
           Talent(6, 'Talent 7', Dateslot(), resp_c),
           Talent(7, 'Talent 8', Dateslot(), resp_c),
           Talent(8, 'Talent 9', Dateslot(), resp_c)) \
    .penalizes_by(2)  # this passes for me; if you put something other than 2, it will raise an AssertionError

Closing since count appears to be working as expected and there are not enough details to diagnose what the issue is.