I'm trying to convert a simple VRP project using drl to constraint streams, and I'm not sure how to replicate the functionality of "insertLogical".
The "demand" for each delivery is determined by the number of days from the previous delivery. (The demand is for liquid in tanks being consumed at a known rate.) In drl I'd pair the deliveries and insertLogical, and also insertLogical for the firstDelivery based on the current state.
Without insertLogical, I'm joining deliveries and using a groupBy for paired deliveries, but I can't see how to do an "Outer Join" to include the first delivery.
I also tried creating a continuous planning style "pre-schedule" delivery and then omitting those from the "planning value range". Which would mean the pairs would always exist, but I have a kludgy mess for preventing a Customer from using a pre-schedule vehicle.
So, is there a way to "insertLogical" or to do an outer join in constraint streams?
No and no.
You could build a constraint collector; see UniConstraintCollector interface or its bi/tri/... variants. This allows you to implement any custom logic in your groups.
Or you could create a shadow variable that would keep track of the first delivery. (In fact, with the new planning list variable, that may be even easier.)
Related
I am trying to assign Timeslots to planningEntities
(containing room, groups, persons handled by constraint streams).
Some of these planning entities has a blockId.
When entity has a blockId the goal is to share timeslot with other entities with the same blockId.
I defined a constraint for this, but I can see that the solver does extremely many unnecessary moves.
public Constraint groupBlockConstraint(ConstraintFactory constraintFactory){
return constraintFactory.forEachUniquePair(Lesson.class,
Joiners.equal(Lesson::getSequenceGroup),
Joiners.filtering((a, b) ->
!Lesson.withoutBlock(a, b)
&& !Lesson.sameTimeslot(a,b)))
.penalize("BlockSequence not in same timeslot", HardSoftScore.ofHard(15));
}
Is there a way to handle this more efficiently?
Constraints do not determine which moves the solver will be trying. Constraints are only used to score solutions which are achieved once moves are already performed.
Therefore if you're seeing moves which in your opinion should not be performed, you need to configure your selectors. Using tabu search could, perhaps, also help here.
That said, without a more detailed question I can not provide a less generic answer.
We are trying to solve a VRP with Optaplanner.
The score calculation runs via constraint streams.
Now I have two vehicles (A and B) and want to schedule two jobs (J1 and J2).
The construction heuristic (FIRST_FIT_DECREASING) schedules J1 to A and J2 to B, what is correct so far.
Now the two jobs also have an attribute "customer", and I want to assign a penalty if the customer of the two jobs is the same but the vehicles are different.
For this purpose, I have created a constraint in the ConstraintProvider that filters all jobs via groupBy that have the same customer but different vehicles.
If I now switch on the FULL_ASSERT_MODE, an IllegalStateException occurs after scheduling J2, because the score that is calculated incrementally is different from the score for the complete calculation.
I suspect this is because the VariableListener, which recalculates the times of the jobs, only tells the ScoreDirector about a change to Job J2 for my shadowvariables and therefore only changes the score part that is related to it.
How can I tell Optaplanner that the score for J1 must also be recalculated? I can't get to job J1 via the VariableListener to tell the ScoreDirector that the score has to be changed here.
Or does this problem require a different approach?
This is a problem that is a bit hard to explain fully. TLDR version: constraint streams only react to changes to objects which are coming from either from(), join() or ifExists(). Changes on objects not coming through these statements will not be caught, and therefore causing score corruptions. Longer explanation follows.
Consider a hypothetical Constraint Stream like this:
constraintFactory.from(Shift.class)
.join(Shift.class)
.filter((shift1, shift2) -> shift1.getEmployee() == shift2.getEmployee())
...
This constraint stream will work just fine, because if you change Shift by setting a different employee, the Shifts will be re-evaluated. They enter the stream via from() and join(), which is how CS knows to re-evaluate Shifts when they change.
Now consider this constraint stream instead:
constraintFactory.from(Shift.class)
.filter(shift -> shift.getEmployee().getName() == "Lukas")
...
This constraint stream will be re-evaluated, if Shift changes. But when the name of Employee changes, the constraint stream will not be re-evaluated; Employee is neither in from() nor in join(), changes to Employee will not trigger re-evaluation of the constraint stream.
In your particular situation, you need to ensure several things:
Variable listeners mark everything as changed that actually changes.
If you modify problem facts, you need to make sure your variable listeners handle that too.
Objects that you want your constraint stream to react to are coming in through from() or a join().
we are using Optaplanner 7.0 beta + Graphhopper for a calculation of shortest paths in a warehouse, where goods have to be collected into boxes by workers (vrptw). Since the business is about collecting online-ordered goods approx. 70% of the items to collect are added to the problem during the day. We use ProblemFactChange to add the incoming order items and already completed order items in the chain are set to immovable (these 'restarts' are performed each full hour). So far everything works.
The question now is about changing restrictions/conditions, that can occur due to unbalanced workload over warehouse-zones. The warehouse is logically divided into areas, to avoid that all workers have to serve all areas (I know your opinion about segmentation of planning problems, but this is, how the work currently is organised). The limited assignment of items to available workers within one zone is currently defined by a hard-constraint.
The new requirement that we are confronted with is, that a worker should be temporary assigned to a different zone, if the workload there is higher compared to his actual zone. Afterwards he can switch back to his original zone. To my understanding an update of the constraint condition would result in hard constraint violations for the previous assigned, locked items, which should be avoided. Are there mechanisms to support temporary changing restrictions or would a SelectionFilter for items help ? (btw: we are using drools).
Hints are welcome, Thank you
Michael
If there are 2 different tenants, each with their own set of employees, tasks, etc and each in their own Solver, then the Borrow Pattern can be used, especially if the employee borrowing involves some human interaction (usually paperwork or a phone call between managers):
Suppose tenant A has an employee called John and tenant B wants to borrow him. Then assign one or more entities from B to John and make them immovable (usually a boolean borrowLocked). Then add the same entities to tenant A. Neither the solver of A nor B will be able to move them (so they won't change), but both of them will take them into account: tenant A won't give John work when he's working for tenant B and tenant B will agree that those entities are assigned (and it won't try to assign John to other entities as it's no in it's value range).
I prepare example with easyScoreCalculatorClass and incrementalScoreCalculatorClass (Java score counting) to solve problem with power consumers and power suppliers (phones and chargers, where voltage must be equal and consumer required ampere must be not greater then supplier provided ampere, and each supplier has cost).
Optaplanner make solution for sample with 2 consumers and one supplier which is not desired as I expected that one supplier can't operate on two consumers in one time and solution must end with at least -1 value fro hard score.
To enforce one-to-namy constraint I see possibility by maintaining map with count how many times suppliers assigned to consumers. Is that one possible solution to enforce constraint in Java code?
Is that possible to organize data or mark fields with annotations so this constraint automatically enforced?
I look to optaplanner: how to enforce planning variables values to be used only once but don't understand how to map Drool code to Java...
Several of the examples have uniqueness constraint that you describe. For example, in the n-queens example all rows must be different. That example has a java score impl too.
OK...I am hoping this is a classic problem that everyone knows the answer to already. I have been building a mysql database (my first one) where the main purpose was to load line-item data from an invoice and related data from the matching remittance and reconcile the two. Basically, everything has been going along fine until I discovered a problem.
Details: I have thus far identified individual invoice line items with a client (to be billed) id, service date, and service type and matching that transaction against the remittance transaction with the same client ID, service date and service type. Unfortunately, there are times (I just discovered) when one client (ID) gets multiple instances of a particular service on the same day and thus my invoices are not unique based on the three components I just mentioned.
There is another piece of info on the invoice (service time) that could be used to make invoice items unique, but the remittance does not include service times (thus I cannot match directly against it using service time). Likewise, the remittance has another piece of info (claims ref number) that uniquely identifies remittance items. But of course, the claims ref number is not on the invoice.
Is there some way to use an intermediate table perhaps that can bridge this gap? Any help, answers or helpful links would be most appreciated. Thanks in advance.
This is perhaps more a business problem then a technical one-- it sounds like there is in fact no reliable way to match up remittances and invoices, unless something like matching on the dollar amount works. If you use an artificial key on the invoice you kind of solve the technical problem but not the business one.
If you can't change the business process at all and there is no technical way to match remittances and invoices, you might be forced to treat all invoices for a customer/service date/service type as a unit; make each invoice a part of that unit, and then group all the remittances and all the invoices that match that unit together.
You can make life easy on yourself and create an Invoice ID and remove the composite key all together.
Any type of fix is going to have an impact on the calling code, as increasing the field count on the composite key implies that this new field needs to be supplied, so I suggest just creating an invoice ID.
Many IT professionals that work with RDBMS will suggest to never use natral keys. Always use a surrogate key (like an auto-increment column)
I agree with #antlersoft (+1), this sounds mostly like a business problem: how to “match up” items within two separate sets of data that cannot be clearly and cleanly matched up with the data provided.
If the “powers that be” (aka your manager/supervisor/project owner) cannot or will not make this decision, and if you have to do something, based on the information provided I’d recommend matching same-day items like so:
lowest invoice-item service time with lowest remittance claims ref number
next-lowest invoice item service time with next-lowest remittance claims ref number
etc.
(So when you have such multiple-per-day items, do you always have the same number of invoice items and remittances? Or is that going to be your next hurdle?)
Once you know how to implement “matching up” items, you then have to implement it by storing the data that supports/defines the assocaition within the database. Assuming tables InvoiceItem and Remittance, you could add (and populate) ServiceTime in the Remittance table, or ClaimsRefNumber in the InvoiceItem table (the latter seems more sensible to me). Alternatively, as most people suggest, you could add a surrogate key to either (or both) tables, and store one’s surrogate key in the other’s table. (Again, I’d store, say, RemittanceId in table InvoiceItem, as presumably you couldn’t have a Remittance without an InvoiceItem – but it depends strongly upon your buseinss logic.)