2d Stock Cutting with CP-SAT . Y values always 0

87 views Asked by At

I've been struggIing to make a 2d Stock cutting solution. I found a Python code that is proven to work and converted it to C#. I get results ofthe X1 values but the Y values are always 0. I checked the log and its really difficult for me to understand this type of programming. Can anyone please help me. The original code is here in the comments https://yetanothermathprogrammingconsultant.blogspot.com/2021/02/2d-bin-packing-with-google-or-tools-cp.html

 List<advRectangle> smallRectangles = new() { };

 smallRectangles.Add(new advRectangle(500, 750, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 1000, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(2000, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 752, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 2000, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(500, 750, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1250, 250, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(250, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(500, 750, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 1000, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(2000, 500, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 750, 0, 0, "1"));
 smallRectangles.Add(new advRectangle(1000, 2000, 0, 0, "1"));
       
 advRectangle BigRect = new advRectangle(2000, 3000, 0, 0, "1");
        
 // Create a CP model
 CpModel model = new CpModel();

 // Data
 int H = Convert.ToInt32(BigRect.UHeight());  // Bin height
 int W = Convert.ToInt32(BigRect.UWidth()); ;  // Bin width

 List<int> h = smallRectangles.Select(sr => Convert.ToInt32(sr.UHeight())).ToList();
 List<int> w = smallRectangles.Select(sr => Convert.ToInt32(sr.UWidth())).ToList();

 int n = h.Count; // Number of items
 int m = 10;     // Max number of bins

 // Variables
 IntVar[] x1 = new IntVar[n];
 IntVar[] x2 = new IntVar[n];
 IntVar[] y1 = new IntVar[n];
 IntVar[] y2 = new IntVar[n];
 
 // Constraints

 long[][] x1Intervals = new long[n][];
 long[][] x2Intervals = new long[n][];
 
             
 for (int i = 0; i < n; i++)
 {
     List <long[]> tempx1 = new List<long[]>();

     for (int j = 0; j < m; j++)
     {
         tempx1.Add(new long[] { j * W, (j+1)*W - w[i] });                   
     }

     x1Intervals = tempx1.ToArray();
     x1[i] = model.NewIntVarFromDomain(Google.OrTools.Util.Domain.FromIntervals(x1Intervals), $"x1[{i}]");
 }
          
 for (int i = 0; i < n; i++)
 {
     List<long[]> tempx2 = new List<long[]>();           
     for (int j = 0; j < m; j++)
     {
         tempx2.Add(new long[] { j * W + w[i], (j + 1) * W });
     }
     x2Intervals = tempx2.ToArray();
     x2[i] = model.NewIntVarFromDomain(Google.OrTools.Util.Domain.FromIntervals(x2Intervals), $"x2[{i}]");
 }


 for (int i = 0; i < n; i++)
 {
     y1[i] = model.NewIntVar(0, H - h[i], $"y1[{i}]");
 }
    
 for (int i = 0; i < n; i++)
 {                
     y2[i] = model.NewIntVar(h[i], H, $"y2[{i}]");
 }


 // Define literals for bin assignment
 BoolVar[][] lit = new BoolVar[n][];
 for (int i = 0; i < n; i++)
 {
     lit[i] = new BoolVar[m];

     for (int j = 0; j < m; j++)
     {
         lit[i][j] = model.NewBoolVar($"lit[{i}][{j}]");
     }
 }

 // Now we assert that every rectangle belongs to exactly one bin.Also we must refine left and right boundaries of every rectangle:

 var bv1 = new List<BoolVar>();
 var lv1 = new List<long>();
 var bv2 = new List<BoolVar>();
 var lv2 = new List<long>();

 var bvy1 = new List<BoolVar>();
 var lvy1 = new List<long>();
 var bvy2 = new List<BoolVar>();
 var lvy2 = new List<long>();


 for (int i = 0; i < n; i++)
 {
     model.Add(Google.OrTools.Sat.LinearExpr.Sum(lit[i]) == 1);
 }
 for (int i = 0; i < n; i++)
 {
     for (int j = 0; j < m; j++)
     {
         bv1.Add(lit[i][j]);
         lv1.Add(j * W);
     }
 
     for (int j = 0; j < m; j++)
     {
         bv2.Add(lit[i][j]);
         lv2.Add((j + 1) * W);
     }

 }
 for (int i = 0; i < n; i++)
 {
     model.Add(Google.OrTools.Sat.LinearExpr.WeightedSum(bv1, lv1) <= x1[i]);
     model.Add(Google.OrTools.Sat.LinearExpr.WeightedSum(bv2, lv2) >= x2[i]);
 

 }

     // Define interval variables
     Google.OrTools.Sat.IntervalVar[] x_interval = new Google.OrTools.Sat.IntervalVar[n];
     Google.OrTools.Sat.IntervalVar[] y_interval = new Google.OrTools.Sat.IntervalVar[n];

 for (int i = 0; i < n; i++)
 {
     x_interval[i] = model.NewIntervalVar(x1[i], w[i], x2[i], "");
     y_interval[i] = model.NewIntervalVar(y1[i], h[i], y2[i], "");
 }
 
       //fixed   
   NoOverlap2dConstraint NoOverlap2Constraint = new 
   NoOverlap2dConstraint(model.Model);
   NoOverlap2Constraint.Proto = model.AddNoOverlap2D().Proto;

   for (int i = 0; i < n; i++)
   {  NoOverlap2Constraint.AddRectangle(x_interval[i], y_interval[i]);}




        
 // Define the objective
 IntVar obj = model.NewIntVar(0, m, "obj");
 for (int i = 0; i < n; i++)
 {
     for (int j = 0; j < m; j++)
     {               
         model.Add(obj >= (j + 1)).OnlyEnforceIf(lit[i][j]);  
     }
 }

 model.Minimize(obj);
       

 // Create a CP solver.
 CpSolver solver = new CpSolver();
 solver.StringParameters = "log_search_progress:true";
 solver.SetLogCallback(LogCallback);
 // Solve the problem
 CpSolverStatus status = solver.Solve(model);

        


 if (status == CpSolverStatus.Optimal)
 {

     // Print the bin and position of each item
     for (int i = 0; i < n; i++)
     {

         smallRectangles[i].X = solver.Value(x1[i]);
         smallRectangles[i].Y = solver.Value(y1[i]);
         Console.WriteLine($"Item {i}: Bin {solver.Value(obj)}, X1: {solver.Value(x1[i])}, Y1: {solver.Value(y1[i])}");
     }
 }
 else
 {
     MessageBox.Show("No optimal solution found.");
 }

 ;
1

There are 1 answers

5
Laurent Perron On

as with your last entry, you create one no_overlap_2d constraint per rectangle. Therefore each constraint is constraining 1 rectangle, and thus has nothing to do.

You need to create one no_overlap_2d constraint and add all rectangles to the same constraint.