optaplanner bin packing result not optimal - optaplanner

I'm a new to optaplanner. trying to implemete a bin packing solution to packing ecomcerce order items into carton containers, we have different containers size to hold all items.
from optaplanner, i'm follow the example case of cloudbalance to implement this bin packing. https://www.optaplanner.org/docs/optaplanner/latest/use-cases-and-examples/cloud-balancing/cloud-balancing.html
when I fist run out the result. seems not a optimized solution , not sure where is wrong in code.
public void run() throws IOException {
SolverFactory<CartonizationSolution> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(CartonizationSolution.class)
.withEntityClasses(OrderItem.class)
.withConstraintProviderClass(CartonizationConstraintProvider.class)
.withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(5L)));
Solver<CartonizationSolution> solver = solverFactory.buildSolver();
CartonizationSolution solution = load();
CartonizationSolution solvedSolution = solver.solve(solution);
ScoreManager<CartonizationSolution, HardSoftScore> scoreManager = ScoreManager.create(solverFactory);
ScoreExplanation<CartonizationSolution, HardSoftScore> cartonizationSolutionHardSoftScoreScoreExplanation = scoreManager.explainScore(solution);
System.out.println(scoreManager.getSummary(solution));
System.out.println("Planning items: " + solution.getOrderItems().size());
System.out.println("Planning cartons: " + solution.getCartonRange().size());
System.out.println("\nSolved CartonizationSolution:\n"
+ toDisplayString(solvedSolution));
}
Total Container be grouped: 4
Type: Small -> 4
CartonContainer#Small#3: 8 items
Volume Usage 97.005356% 13037520/13440000
Weight Usage 34.233334% 5135/15000
CartonContainer#Small#1: 10 items
Volume Usage 99.417336% 13361690/13440000
Weight Usage 24.633333% 3695/15000
CartonContainer#Small#4: 11 items
Volume Usage 75.845314% 10193610/13440000
Weight Usage 27.333334% 4100/15000
CartonContainer#Small#2: 12 items
Volume Usage 99.58103% 13383690/13440000
Weight Usage 91.64% 13746/15000
Total Volum: 53760000
public class CartonizationConstraintProvider implements ConstraintProvider {
#Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
requiredWeightTotal(constraintFactory),
requiredVolumeTotal(constraintFactory),
computerCost(constraintFactory)
};
}
Constraint requiredWeightTotal(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(OrderItem.class)
.groupBy(OrderItem::getContainer, sum(OrderItem::getWeight))
.filter((container, requiredWeight) -> requiredWeight > container.getMaxWeight())
.penalize(HardSoftScore.ONE_HARD,
(container, requiredWeight) -> requiredWeight - container.getMaxWeight())
.asConstraint("requiredWeightTotal");
}
Constraint requiredVolumeTotal(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(OrderItem.class)
.groupBy(OrderItem::getContainer, sum(OrderItem::getVolume))
.filter((container, requiredVolume) -> requiredVolume > container.getMaxVolume())
.penalize(HardSoftScore.ONE_HARD,
(container, requiredVolume) -> requiredVolume - container.getMaxVolume())
.asConstraint("requiredVolumeTotal");
}
Constraint computerCost(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(CartonContainer.class)
.ifExists(OrderItem.class, equal(Function.identity(), OrderItem::getContainer))
.penalize(HardSoftScore.ONE_SOFT, CartonContainer::getMaxVolume)
.asConstraint("overallVolume");
}
}
Similar data running with google's OR-Tools.
I can get bellow result.
Number of Items be planning: 41
Number of Carton be planning: 30
<generator object cartonize.<locals>.<genexpr> at 0x1057f3530>
Bin number #0 Small
Items packed: 9
Total weight: 42% 6.2909999999999995 / 15.0
Total volume: 99% 13320.85 / 13440.0
Bin number #15 Small
Items packed: 14
Total weight: 78% 11.686000000000002 / 15.0
Total volume: 99% 13269.66 / 13440.0
Bin number #25 Medium
Items packed: 18
Total weight: 58% 8.698999999999998 / 15.0
Total volume: 99% 23386.0 / 23520.0
Number of bins used: 3
Total volume 50400.0
Time = 1138 milliseconds
Should a close solution result as OR Tools, as the total volume is lower

Please enable subpillar change and swap moves.

Related

Solver doesnt try to optimize solution ( one hard constraint, one soft constraint )

I got 2 constraints :
one HARD ( 100 score )
one SOFT ( 100 score )
When i run the solver, it's like he just try to resolve the hard one, and doesn't look for the soft one. I have no softScore, optaplanner return a 0Hard/0Medium/0Soft.
My HARD constraint is : a worker can't work more than 5 days in a row
My SOFT constraint is : try to put the most of working days possible ( calculated by hours of work )
My test is for two weeks. A worker need to be above 66 hours of work, is he is under we penalize more.
Here the two constraints in JAVA:
the SOFT one :
private Constraint averageWorkingHours(ConstraintFactory constraintFactory) {
return constraintFactory
.from(WorkingDay.class)
.filter((wd) -> wd.isFreeToWork())
.groupBy(WorkingDay::getAgent,
WorkingDay::hasBreakDayBySolver,
// hasBreakDayBySolver return if the solver has put my
#PlanningVariable ( a breakDay ) in the WorkingDay
ConstraintCollectors.count())
.filter((agent, hasBreakDayBySolver, count) -> {
return !hasBreakDayBySolver;
})
.penalizeConfigurable(AVERAGE_HOURS_WORKING, ((agent, hasBreakDayBySolver, count) -> {
// a worker need to be above 66 hours of work for 2 weeks
// We penalize more if a worker is under the average of working hours wanted for two weeks ( 66 )
if(count * 7 < 66){ // count * hours worked for one day
return (66 - count * 7) * 2 ;
}
else{
return count * 7 - 66;
}
}));
}
the HARD one :
private Constraint fiveConsecutiveWorkingDaysMax(ConstraintFactory constraintFactory) {
return constraintFactory
.from(WorkingDay.class)
.filter(WorkingDay::hasWork)
.join(constraintFactory.from(WorkingDay.class)
.filter(WorkingDay::hasWork),
Joiners.equal(WorkingDay::getAgent),
Joiners.greaterThan(wd->wd.getDayJava()),
Joiners.filtering((wd1, wd2)->{
LocalDate fourDaysBefore = wd1.getDayJava().minusDays(4);
Boolean wd2isAfterFourDaysBeforeWd1 = wd2.getDayJava().compareTo(fourDaysBefore) >= 0;
return wd2isAfterFourDaysBeforeWd1;
})
)
.groupBy((wd1, wd2) -> wd2, ConstraintCollectors.countBi())
.filter((wd2, count) -> count >= 4)
.penalizeConfigurable(FIVE_CONSECUTIVE_WORKING_DAYS_MAX,((wd2, count)-> count - 3));
}
I hope my explanations are clear.
Thanx !
Unit test your constraints, using a ConstraintVerfier. See also this short video by Lukas.
Verify that your #ConstraintWeight for that soft constraint in your dataset isn't zero.

For loop must have an iterator()

I need this service in which if the person stays for longer than 30 minutes, they have to pay an extra $10 every 15 minutes (and for the fraction of the 15 as well).
I designed it like this so far:
var checkInTime: Calendar
val totalTime: Long
get() = (Calendar.getInstance().timeInMillis - checkInTime.timeInMillis) / MIN_IN_MILISEC
fun getTime(totalTime:Long): Int{
var finalPrice = 0
var initialPrice = 20
if(totalTime<31){
finalFee = initialPrice
} else {
val extraPrice = 10
val extraTime = 15
finalFee = initialPrice
for(extraTime in totalTime){
finalFee += extraTime
}
return finalFee
}
I get the error "For loop must have an iterator()" when I try to loop through the totalTime when it's more than 30 minutes so that I can add $10 every 15 extra minutes. I need some help as to how to add to the finalFee every extra 15 minutes the person stays since my method is not working.
Thank you.
Let's take a look at your getTime function:
You're using a Long as totalTime. You can measure it in minutes to simplify your calculation (since all time values are measured in minutes). Since a Long type in Kotlin stores a integer up to 9,223,372,036,854,775,807 and no soul on Earth will use your service for that long (this represents 17 billion milleniums), you can just use an Int.
You're not declaring the finalFee variable, thus the code will raise an
"Unresolved reference" error. Since you're not using the finalPrice variable, I'm assuming you wanted to use this instead.
You're trying to iterate over a numeric value (in this case, totalTime, which is a Long). You can iterate over each element of a List, but how would you iterate over each element of an integer? I'm assuming you want to do a certain action totalTime number of times. In this case, you would use ranges.
You're also not using the variables extraPrice and extraTime.
There's code that's common to both if-else conditions (finalPrice = initialPrice), so you can extract that to outside the if-statement.
Refactoring your function:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime >= 30) {
(0 until totalTime).forEach {
finalPrice += 15
}
}
return finalPrice
}
It's shorter, but still doesn't do what it's supposed to: let's suppose totalTime is equal to 45. The person got 30 minutes costing $20 and only have to pay $10 for every 15 minutes, therefore will only pay $30 total. Your function is considering that the person will have to pay $15 for every minute they stayed, because it uses a for-loop that goes from 0 to totalTime. For that, you need a for-loop that goes from 30 (the time limit) from the total time (the totalTime) every 15 minutes:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime > 30) {
(30 until totalTime step 15).forEach {
finalPrice += 10
}
}
return finalPrice
}
Better yet, you don't even need a for-loop, you can just use maths:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime > 30) {
finalPrice += ((totalTime - 30) / 15) * 10
// ^^^^^^^^^^^^^^^^ Get the exceeding time
// ^^^^^^^^^^^^^^^^^^^^^^^ How many 15 minutes are there?
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Put $10 for every 15 minutes
}
return finalPrice
}
The last part: your question said you need to consider the fraction of 15 as well. Therefore, you need to use a real value, not an integer. Let's change it to a Double:
fun getTime(totalTime: Int): Double {
var finalPrice = 20.0
if (totalTime > 30) {
finalPrice += ((totalTime - 30) / 15.0) * 10
}
return finalPrice
}
Let's test your function:
fun main() {
println(getTime(0)) // Outputs 20.0
println(getTime(10)) // Outputs 20.0
println(getTime(30)) // Outputs 20.0
println(getTime(45)) // Outputs 30.0
println(getTime(60)) // Outputs 40.0
println(getTime(70)) // Outputs 46.666...
}

How to meet properly if condition

I'm having a problem with this task. My goal is to calculate car price providing its mileage.
The formula is:
price lowers by 200$ for every 10000 kilometers that the car passed. For example, for 19999 km the price decreases by 200 dollars, but for 20000 km the price lowers by 400 dollars. And additionally the prices lowers due to car's age multiplied by 200.
What I did is:
when (input) {
in 0..9999 -> {
moneyToRemove = 2000
price -= moneyToRemove
}
else -> {
var counter = 0
moneyToRemove = old * 2000
for (x in 10000..input) {
if (x % 10000 == 0 ) {
counter++
if (x == input) {
println(counter)
price = price - moneyToRemove - counter*200
}
}
}
}
}
input - mileage provided from scanner
In first when condition moneyToRemove variable is set to 2000 ( 5years *200)
In else condition I tried to count how many extra dollars will lower the car price using counter. But then I have a problem with e.g. value 299 999. The counter in debugger mode shows 29 which is correct, but I can't meet the second condition and properly count car's price. Can you help?
This is from JetBrains academy Kotlin dev track.
You don't need any if conditions for that. You can put it in a simple formula
import kotlin.math.*
fun main() {
var originalprice = 30000; // the original price
var miles = 12345.6; // the mileage
var age = 7; //the age in years
var reducedprice = originalprice -
(floor(miles / 10000.0)) * 200 - //substract 200$ for each full 10000 miles
age * 200 //substract 200$ foreach year
print(reducedprice);
}
floor(miles/ 10000.0) will divide the miles by 10000 and round it downwards to the next integer. So it will be 0 for 0-9999, 1 for 10000 - 19999 and so on ... floor
If miles is an integer value, you don't even need floor, because the integer division will only return whole numbers. Ie 9999/10000 = 0, 19999/10000 = 1 and so on ...
var reducedprice = originalprice -
(miles / 10000) * 200 - //substract 200$ for each full 10000 miles
age * 200 //substract 200$ foreach year
EDIT
Eventhough I consider your approach as quite complicated and not easily maintainable
You have code duplication for the age part,
You do a lot of unnecessary % calculations for each value from 10000 up to your input
The when isn't necessary, because all the code exectued in the first branch is also executed in the second branch. Just could just do the loop, starting at 10000, so if the input is < 10000 it won't be executed ...
But all in all, your approach will in principle work too. But there are two issues with it
You don't consider the age of the car, when the mileage is below 10000, but you just withdraw a fixed amount.
When the mileage is above 10000, you calculate the reduced price only if the input is an exact multiple of 10000. You must put that calculation after the loop.
when (input) {
in 0..9999 -> {
moneyToRemove = old * 2000
price -= moneyToRemove
}
else -> {
moneyToRemove = old * 2000
for (x in 10000..input) {
if (x % 10000 == 0 ) {
moneyToRemove += 200
}
}
println(counter)
price -= moneyToRemove
}
}

Java 8 Streams: map in stages

Is it possible to carry out mappings on a stream in stages without creating intermediary collections?
I'm working with Selenium and mapping streams of WebElements. It's important to do them in batches to avoid StaleReferenceException being thrown since the page dynamically updates itself and some of the map operations take a relatively long time to complete.
Problem (simplified):
public class StreamTest {
public static void main(String[] args) {
String[] input = new String[] {"1", "2", "3", "4", "5"};
List<String> list = map(input).collect(Collectors.toList());
}
private static Stream<String> map(String[] input) {
return Arrays.stream(input).parallel()
.map(s -> {
String ret = s + "0";
System.out.println(String.format("%10s%10s", s, ret));
return ret;
})
.map(s -> {
String ret = s + "0";
System.out.println(String.format("%10s%10s", s, ret));
return ret;
})
.map(s -> {
String ret = s + "0";
System.out.println(String.format("%10s%10s", s, ret));
return ret;
});
}
}
Sample Output:
3 30
4 40
30 300
1 10
5 50
10 100
2 20
50 500
300 3000
40 400
500 5000
20 200
100 1000
400 4000
200 2000
Desired output:
1 10
2 20
3 30
4 40
5 50
10 100
20 200
30 300
40 400
50 500
100 1000
200 2000
300 3000
400 4000
500 5000
Current Solution:
private static Stream<String> map(String[] input) {
return Arrays.stream(input)
.map(s -> s + "0")
.collect(Collectors.toList()).stream()
.map(s -> s + "0")
.collect(Collectors.toList()).stream()
.map(s -> s + "0");
}
Is there a way to achieve this without creating a dummy collection between each call to map?
Note: I need to use a stream with n calls to map rather than a sequence of n for-loops.
This is not how streams have been designed to behave.
A sequence of map operations is applied without creating intermediate collections, only the terminal operation decides what the result will be (a collection or a single value).
But to work in batches (applying the first map operation to all input, then applying the second map operation to all first intermediate results, and so on ) you need intermediate collections.
This seems to work:
String[] input = new String[] {"1", "2", "3", "4", "5"};
Stream.of("", "0", "00")
.flatMap(suffix -> Stream.of(input)
.map(prefix -> prefix + suffix)
.map(s -> String.format("%10s%10s", s, s + "0")))
.forEach(System.out::println);
The bad ordering is due to the parallel() call, which can probably be avoided.
To be honest, no idea what you are trying to do. Here is something that would conform to your sample output:
private static Stream<String> map(String[] input) {
return Stream.of("", "0", "00")
.flatMap(x -> Arrays.stream(input)
.map(y -> y + x)
.peek(z -> System.out.println(z + " " + z + "0"))
.map(z -> z + "0"))
.skip(2 * input.length);
}
The out of order results that you see are because you are using parallel and intermediate operations have no defined order of execution, only terminal do (unless otherwise stated - forEach for example)

CPU Usage (%) MBean on Sun JVM

The overview tab of a process on jconsole shows me the CPU Usage percentage. Is there a MBean that gives me this value? What is its ObjectName?
Update: In Java 7 you can do it like so:
public static double getProcessCpuLoad() throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem");
AttributeList list = mbs.getAttributes(name, new String[]{ "ProcessCpuLoad" });
if (list.isEmpty()) return Double.NaN;
Attribute att = (Attribute)list.get(0);
Double value = (Double)att.getValue();
if (value == -1.0) return Double.NaN;
return ((int)(value * 1000) / 10.0); // returns a percentage value with 1 decimal point precision
}
----- original answer below -----
In Java 7 you can use the hidden methods of com.sun.management.OperatingSystemMXBean:
getProcessCpuLoad() // returns the CPU usage of the JVM
getSystemCpuLoad() // returns the CPU usage of the whole system
Both values are returned as a double between 0.0 and 1.0 so simply multiply by 100 to get a percentage.
com.sun.management.OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
System.out.println(osBean.getProcessCpuLoad() * 100);
System.out.println(osBean.getSystemCpuLoad() * 100);
Since these are hidden, undocumented, methods that exist in com.sun.management.OperatingSystemMXBean package and not in the java.lang.management.OperatingSystemMXBean there is a risk that they will not be available in some JVMs or in future updates, so you should decide if you're willing to take that risk or not.
see https://www.java.net/community-item/hidden-java-7-features-%E2%80%93-system-and-process-cpu-load-monitoring for more.
There does not seem to be a direct MBean within ManagementFactory. The closest is http://java.sun.com/javase/6/docs/api/java/lang/management/OperatingSystemMXBean.html#getSystemLoadAverage() which can be used to calculate the CPU used by the whole system.
However this URL has suggested a method based on the source code of jconsole
I modified a code from internet, like this, then I tested that and the result almost match the linux ps command's result.
/** below is the code */
public float getCpuUsed() {
/** get a MXBean */
com.sun.management.OperatingSystemMXBean osMXBean =
(com.sun.management.OperatingSystemMXBean)
ManagementFactory.getOperatingSystemMXBean();
/** set old timestamp values */
long previousJvmProcessCpuTime = osMXBean.getProcessCpuTime();
int sleepTime = 350;
/** sleep for a while to use to calculate */
try {
TimeUnit.MILLISECONDS.sleep(sleepTime);
} catch (InterruptedException e) {
logger.error("InterruptedException occurred while MemoryCollector sleeping...");
}
/** elapsed process time is in nanoseconds */
long elapsedProcessCpuTime = osMXBean.getProcessCpuTime() - previousJvmProcessCpuTime;
/** elapsed uptime is in milliseconds */
long elapsedJvmUptime = sleepTime ;
/** total jvm uptime on all the available processors */
//long totalElapsedJvmUptime = elapsedJvmUptime * osMXBean.getAvailableProcessors() ;
long totalElapsedJvmUptime = elapsedJvmUptime;
//System.out.println("echo cpu processors num " + osMXBean.getAvailableProcessors());
/** calculate cpu usage as a percentage value
to convert nanoseconds to milliseconds divide it by 1000000 and to get a percentage multiply it by 100 */
float cpuUsage = elapsedProcessCpuTime / (totalElapsedJvmUptime * 10000F);
return (float)(Math.round(cpuUsage*10)/10);
}
Iff you are using UNIX based OS then it's way much easier
final OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
if (mxBean instanceof UnixOperatingSystemMXBean) {
return ((UnixOperatingSystemMXBean) mxBean).getSystemCpuLoad() * 100.0;
}