I am reading through the vehicle routing example (v 7.25.0) and have stumbled on some confusion regarding what is a problem fact vs planning entity.
The documentation in section 4.3.5.4 states:
A planning variable that is chained either:
- Directly points to a problem fact (or planning entity), which is called an anchor.
- Points to another planning entity with the same planning variable, which recursively points to an anchor.
And then right below it also states:
Every chain always has exactly one anchor. The anchor is a problem fact, never a planning entity.
This conflicting information leads to my confusion #1: is an anchor always a problem fact, or can it be a planning entity?
The reason I ask is because Vehicle (the anchor) implements Standstill which is defined in the vehicleRoutingSolverConfig.xml as:
<entityClass>org.optaplanner.examples.vehiclerouting.domain.Standstill</entityClass>
which leads me to believe that the Vehicle would "inherit" the planning entity property.
But then, I tried creating a drools rule that sets a hard constraint on the max # of customers associated to a vehicle using an instance method on Vehicle that returns the count by walking nextCustomer shadow variable until null, like:
when
Vehicle(totalCustomers > 5, $totalCustomers : totalCustomers)
then
scoreHolder.addHardConstraintMatch(kcontext, 5 - $totalCustomers)
and it doesn't seem to work: the hard constraints are violated and the hard score stays 0 (there are no other hard constraints, this was just a test -- also, I had the planner run for a very long time using CH + local search and breakpoints showed this method returning > 5 at various points in time including the final solution, so it wasn't just a configuration issue).
What am I missing?
Good point, thank you for pointing this mistake out in the docs.
I am fixing this as such:
Wrong: Every chain always has exactly one anchor. The anchor is a problem fact, never a planning entity.
Correct: Every chain always has exactly one anchor. The anchor is never an instance of the planning entity class that contains the chained planning variable.
Related
I'm modifying the vehicle routing Optaplanner example. Vehicles are replaced with individuals who have to travel around the city, but they can do so using different modes of transportation. So I have an attribute on the anchor (Vehicle in the example, Employee in my modified code) called modeOfTransportation.
When calculating the arrival time using the custom shadow variable listener from the example, I want to take the mode of transportation into account of course. But, when Optaplanner starts initialising my planning entities (consumers), it seems that they at first are not connected to an anchor. So I can't get my mode of transportation, and everything breaks down.
Any ideas on how to proceed?
Below is what I want to accomplish.
shadowVisit is my planning entity, shadowVisit.getEmployee() should give me the anchor.
Doing a shadowVisit.getEmployee()==null check seems to hang the entire solving process.
arrivalTime =
previousStopDepartureTime.plus(
shadowVisit.getLocation().getDistanceFrom(
shadowVisit.getPreviousStop().getLocation(), shadowVisit.getEmployee().getModeOfTransportation())
OK, so I figured out what the issue was.
My problem is overconstrained, and I implemented a dummy employee as the solution (see optaplanner: modifying vehicle routing to let customers be not served)
I had not set a modeOfTransportation for my dummy, causing the null pointers. Sometimes is just good to write down a problem, makes you think hard enough to solve it!
Thank you very much for your input Geoffrey
That's strange, because the chain principles guarantee that every chain has an anchor (see below).
Maybe your #CustomShadowVariable's sources attribute doesn't include the anchor shadow var, and your custom variable listener is called before the anchor shadow variable listener is called.
OptaPlanner guarantees that it will call one type of variable listener for all domain classes before calling the next type. The order of those types of variable listeners is determined by that sources attribute (see second image).
Which is the performant implementation to known on the anchor which is the current last entity of its chain?
Right now I traverse all the chain up to the end to get it which is O(n) in terms of performance.
Is there a way to attach a VariableListener on the Anchor to be notified en every chain update?
NOTE: My entities have a reference to previous and next (the later implemented by a VariableListener on the entity)
Thanks.
Create a #CustomShadowVariable with the source set to:
the #PlanningVariable (for example Customer.previousStandstill) to be notified of every chain update
the #AnchorShadowVariable (for example Customer.vehicle) to be notified of every planning entity switching to another anchor
i want a change on one planning variable on one planning entity to affect the same variable on certain other instances of the same planning enitity. in my case, I have a planning entity called taskResourceAllocation that has a planning variable called taskStartIndex, and i want a change to the taskStartIndex on one taskResourceAllocation to be reflected on the other taskResourceAllocations that belong to the same task, and to all the taskResourceAllocations that belong to other tasks of the same activity.
Can i annotate the taskStartIndex with both a #planningVariable and a #CustomShadowVariable ?
i saw the exam example in the sources which uses casting from exam to leading exam and following exam and shadows the change of period from the leading exam to the following exams and thought about going with that approach but i'm not sure that it suits me, as i am afraid of duplicating entities...
Sounds like you want a genuine planning variable on Task (which is then a planning entity too, don't forget it in your solver config) and then have a shadow variable on every taskResourceAllocation of that task. I doubt if that even needs to be a shadow variable, you might just be able to do getTask().getStartIndex().
But a genuine planning variable cannot be a shadow variable at the same time.
I learned in another question that BaseUnits must be singletons. This has a number of disadvantages, including making client code a bit harder to work with (you have to store the singleton somewhere and provide access to it), and making it harder to serialize code e.g. via Fuel.
What is the benefit of this constraint? (I'm assuming it's so that users are safe if they load two Aconcagua clients which define e.g. BaseUnit subclass: #Pound differently)
In practice, is it worth it, or would it be better to treat BaseUnits like value objects? Especially in light of the fact that the paper itself uses expressions like 1 dollar, which already precludes units with the same name.
I wrote something about it in the other post.
Units are not really Singletons (as Singleton is defined in the gang of four book), and the idea is not to create a class per unit but to instantiate BaseUnit or DerivedUnit, etc., per unit you need.
So, for example you can create:
meter := BaseUnit named: 'meter'.
centimeter := ProportionalDerivedUnit basedUnit: meter convertionFactor: 1/100
named: 'centimeter'.
And then write:
1*meter = (100*centimeter)
that will return true.
As I post in the other question, equality is defined as the default, therefore identity is used.
So, you can do two things to make equality works:
To have well know objects (using global variables or a global root object to access them as Chalten does)
Modify #= in Unit and make two units equal if the have the same name (or create a subclass with this definition of #=)
The main reason to use the default #= implementation are:
It is the more generic solution
Units (in real life) are unique, so it make sense to be unique in the model
It make sense to have one "meter" object instead of creating one each time you need it.
The main disadvantage is like you see, that the first time you see it could be kind of problematic to understand, but again, you only need to have a way to access to the object and problem solved.
Regarding Fuel, the problem can be solved saving the root object that defined all units (like TimeUnit in Chalten) or implementing option 2) :-)
Hope this help! Let me know if you have more questions!
I encountered this a couple of times now, and i wondered what is the OO way to solve circular references. By that i mean class A has class B as a member, and B in turn has class A as a member.
One example of this would be class Person that has Person spouse as a member.
Person jack = new Person("Jack");
Person jill = new Person("Jill");
jack.setSpouse(jill);
jill.setSpouse(jack);
Another example would be Product classes that have some Collection of other Products as a member. That collection could for example be products that people who are interested in this product might also be interested in, and we want to upkeep that list on a per-product base, not on same shared attributes (e.g. we don't want to just display all other products in the same category).
Product pc = new Product("pc");
Product monitor = new Product("monitor");
Product tv = new Product("tv");
pc.setSeeAlso({monitor, tv});
monitor.setSeeAlso({pc});
tv.setSeeAlso(null);
(these products are just for making a point, the issue is not about wether or not certain products would relate to each other)
Would this be bad design in OOP in general? Would/should all OOP languages allow this, or is it just bad practice? If it's bad practice, what would be the nicest way of solving this?
The examples you give are (to me, anyway) examples of reasonable OO design.
The cross-referencing issue you describe isn't an artifact of any design process but a real-life characteristic of the things you're representing as objects, so I don't see there's a problem.
What have you encountered that has given you the impression that this approach is bad-design?
Update 11 March:
In systems that lack garbage collection, where memory management is explicitly managed, one common approach is to require all objects to have an owner - some other object responsible for managing the lifetime of that object.
One example is Delphi's TComponent class, which provides cascading support - destroy the parent component, and all owned components are also destroyed.
If you're working on such a system, the kinds of referential loop described in this question may be considered poor design because there's no clear owner, no one object responsible for managing lifetimes.
The way that I've seen this handled in some systems is to retain the references (because they properly capture the business concerns), and to add in an explicit TransactionContext object that owns everything loaded into the business domain from the database. This context object takes care of knowing which objects need to be saved, and cleans everything up when processing is complete.
It's not a fundamental problem in OO design. An example of a time it might become a problem is in graph traversal, for instance, finding the shortest path between two objects - you could potentially get into an infinite loop. However, that's something you would have to consider on a case-by-case basis. If you know there could be cross-references in a case like that, then code some checks in to avoid infinite loops (for instance, maintaining a set of visited nodes to avoid re-visiting). But if there's no reason it could be a problem (such as in the examples you gave in your question), then it's not bad at all to have such cross-references. And in many cases, as you've described, it's a good solution to the problem at hand.
I do not think this is an example of cross referencing.
Cross referencing usually pertains to this case:
class A
{
public void MethodA(B objectB)
{
objectB.SomeMethodInB();
}
}
class B
{
public void MethodB(A objectA)
{
objectA.SomeMethodInA();
}
}
In this case each object kind of "reaches in" to each other; A calls B, B calls A, and they become tightly coupled. This is made even worse if A and B are in different packages/namespaces/assemblies; in many cases those would create compile time errors as assemblies are compiled linearly.
The way to solve that is to have either object implement an interface with the desired method.
In your case you only have one level of "reaching in":
public Class Person
{
public void setSpouse(Person person)
{ ... }
}
I do not think this is unreasonable, nor even a case of cross-referencing/circular references.
The main time this is a problem is if it becomes too confusing to cope with, or maintain, as it can become a form of spaghetti code.
However, to touch on your examples;
See Also is perfectly valid if this is a feature you need in your code - it is a simple list of pointers (or references) to other items a user may be interested in.
Similarily it is perfectly valid to add spouse, as this is a simple real world relationship that would not be confusing to someone maintaining your code.
I have always seen it as a potential code smell, or perhaps a warning to take a step back and rationalise what I am doing.
As for some systems finding recursive relationships in your code (mentioned in a comment above), these can come up regardless of this sort of design. I have recently worked on a metadata capture system that had recursive 'types' of relationships - i.e Columns being logically related to other columns. It needs to be handled by the code trying to parse your system.
I don't think the circular references as such are a problem.
However, putting all those relationships inside objects may add too much clutter, so you may instead want to represent them externally. E.g. you might use a hash table to store relationships between products.
Referencing other objects is not a real bad OO design at all. It's the way state is managed within each object.
A good rule of thumb is the Law of Demeter. Look at this perfect paper of LoD (Paperboy and the wallet): click here
One way to fix this is to refer to other object via an id.
e.g.
Person jack = new Person(new PersonId("Jack"));
Person jill = new Person(new PersonId("Jill"));
jack.setSpouse(jill.getId());
jill.setSpouse(jack.getId());
I'm not saying it is a perfect solution, but it will prevent circular references. You are using an object instead of a object reference to model the relationship.