protected static function createPointsFromArray($data) { $points = []; $pairCount = count($data) / 2; $i = 1; foreach ($data as $display_id => $pointInfo) { $id = $display_id == 0 ? Point::DEPOT_ID : $i++; $newPoint = new Point(['id' => $id, 'display_id' => $display_id, 'x' => floatval($pointInfo[0]), 'y' => floatval($pointInfo[1]), 'box_dimensions' => isset($pointInfo[2]) ? ['x' => floatval($pointInfo[2]), 'y' => floatval($pointInfo[3]), 'z' => floatval($pointInfo[4])] : null, 'box_weight' => isset($pointInfo[5]) ? floatval($pointInfo[5]) : null, 'combinatorial_value' => $id]); if ($id == Point::DEPOT_ID) { $newPoint->setType(Point::TYPE_DEPOT); $newPoint->setPairId(Point::DEPOT_ID); $newPoint->setBoxWeight(0); $depot = $newPoint; } else { $isPickup = $id <= $pairCount; $newPoint->addData(['type' => $isPickup ? Point::TYPE_PICKUP : Point::TYPE_DELIVERY, 'pair_id' => $isPickup ? $id + $pairCount : $id - $pairCount]); } $points[$id] = $newPoint; } // assign to each delivery point box weight = -1*<box weight of correspoding pickup> // also validate all points foreach ($points as $point) { if ($point->getType() == Point::TYPE_DELIVERY) { $point->setBoxWeight(-$points[$point->getPairId()]->getBoxWeight()); $point->setBoxDimensions($points[$point->getPairId()]->getBoxDimensions()); } if ($point->isInvalid()) { throw new \Exception("Point #" . $point->getId() . " is invalid: " . print_r($point->getValidationErrors(), true)); } } return count($points) > 1 ? $points : reset($points); }
public function getDistanceBetweenPoints(\Litvinenko\Combinatorics\Pdp\Point $firstPoint, \Litvinenko\Combinatorics\Pdp\Point $secondPoint) { $id1 = $firstPoint->getId(); $id2 = $secondPoint->getId(); if (!isset($this->distanceCache[$id1][$id2])) { $this->distanceCache[$id1][$id2] = $this->_getDistanceBetweenPoints($firstPoint, $secondPoint); } return $this->distanceCache[$id1][$id2]; }
public static function readPointsFromFile($filename) { $points = []; if (file_exists($filename)) { $pointData = preg_split("/\\r\\n|\\r|\\n/", file_get_contents($filename)); $count = (int) $pointData[0]; unset($pointData[0]); foreach ($pointData as $row) { try { $pointInfo = explode(' ', $row); $id = $pointInfo[0] == 'depot' ? Point::DEPOT_ID : (int) $pointInfo[0]; $newPoint = new Point(['id' => $id, 'x' => floatval($pointInfo[1]), 'y' => floatval($pointInfo[2]), 'box_dimensions' => isset($pointInfo[3]) ? ['x' => floatval($pointInfo[3]), 'y' => floatval($pointInfo[4]), 'z' => floatval($pointInfo[5])] : null, 'box_weight' => isset($pointInfo[6]) ? floatval($pointInfo[6]) : null, 'combinatorial_value' => $id]); if ($id == Point::DEPOT_ID) { $newPoint->setType(Point::TYPE_DEPOT); $newPoint->setPairId(Point::DEPOT_ID); $newPoint->setBoxWeight(0); $depot = $newPoint; } else { $isPickup = $id <= $count / 2; $newPoint->addData(['type' => $isPickup ? Point::TYPE_PICKUP : Point::TYPE_DELIVERY, 'pair_id' => $isPickup ? $id + $count / 2 : $id - $count / 2]); $points[$id] = $newPoint; } } catch (Exception $e) { throw new \Exception("Can't read row " . key($pointData) . ": " . $e->getMessage()); } } // assign to each delivery point box weight = -1*<box weight of correspoding pickup> // also validate all points foreach ($points as $point) { if ($point->getType() == Point::TYPE_DELIVERY) { $point->setBoxWeight(-$points[$point->getPairId()]->getBoxWeight()); $point->setBoxDimensions($points[$point->getPairId()]->getBoxDimensions()); } // if ($point->isInvalid()) // { // throw new \Exception ("Point #" . $point->getId() . " is invalid: " . print_r($point->getValidationErrors(), true)); // } } // validate depot // if ($depot->isInvalid()) // { // throw new \Exception ("Depot is invalid: " . print_r($depot->getValidationErrors(), true)); // } return ['points' => $points, 'depot' => $depot]; } else { throw new \Exception("File {$filename} does not exist!"); } }
public function getSolution() { // $this->validate(); // $this->getHelper()->validateObjects($this->getPoints()); // $this->_checks=0; $generator = new Generator(['tuple_length' => Point::getPointCount($this->getPoints()), 'generating_elements' => Helper::getGeneratorDataFromPoints($this->getPoints()), 'weight_capacity' => $this->getWeightCapacity(), 'load_area' => $this->getLoadArea(), 'precise' => $this->getPrecise(), 'metrics' => $this->getEvaluator()->getMetrics(), 'initial_object' => Helper::getGeneratorDataFromPoints([$this->getDepot()]), 'log_steps' => true]); $pointSequences = Helper::getPointSequencesFromGeneratorData($generator->generateAll()); $bestPointSequence = null; foreach ($pointSequences as &$pointSequence) { $pointSequence[] = $this->getDepot(); $currentCost = $this->_getCost($pointSequence); if (is_null($bestPointSequence) || $this->_compareCosts($currentCost, $bestCost) === 1) { // if needed, check 3D constraints and skip path if it's incorrect if ($this->getData('check_final_loading')) { if (!$this->canLoad($pointSequence)) { continue; } } $bestPointSequence = $pointSequence; $bestCost = $currentCost; } } $this->setGeneratedPointSequences($pointSequences); // echo $this->_checks."\n"; return new Path(['points' => $bestPointSequence]); }
protected function _generateNestedPointSequences($node) { $pointSequence = $node->getContent()->getPoints(); $generator = new Generator(['tuple_length' => Point::getPointCount($this->getPoints()), 'generating_elements' => Helper::getGeneratorDataFromPoints($this->getPoints()), 'current_path' => $node->getContent(), 'weight_capacity' => $this->getWeightCapacity(), 'load_area' => $this->getLoadArea()]); // $generator->validate(); $points = Helper::getGeneratorDataFromPoints($pointSequence); $result = Helper::getPointSequencesFromGeneratorData($generator->generateNextObjects($points)); if ($result) { // hack: if all PDP points except of depot are present, add depot $nodeHasAllPointsExceptOfDepot = Helper::pointSequenceIncludesAllPickupsAndDeliveries(reset($result), $this->getPoints()); if ($nodeHasAllPointsExceptOfDepot) { foreach ($result as &$resultPointSequence) { $resultPointSequence = array_merge($resultPointSequence, [$this->getDepot()]); } } } return $result; }
public function canLoad($pointSequence, $pythonFile, $loadArea, $weightCapacity, $allPoints) { $result = false; $points = self::removeDepotFromPointSequence($pointSequence); $boxFileName = dirname($pythonFile) . '/boxes.txt'; if (!$this->getBoxesFileIsFilled()) { file_put_contents($boxFileName, IO::getBoxesTextForExternalPdpHelper($allPoints)); $this->setBoxesFileIsFilled(true); } if (!file_exists($pythonFile)) { throw new \Exception("Python file '{$pythonFile}' does not exist!"); } $cmdString = "python {$pythonFile}" . " -b {$boxFileName}" . " -n " . (int) (count($allPoints) / 2) . " -c \"" . implode(' ', $loadArea) . ' ' . $weightCapacity . "\"" . " -r \"" . implode(' ', Point::getPointIds($points)) . " 1\"" . " -p"; $cmdResult = exec($cmdString); // echo $cmdResult . "\n"; $result = $cmdResult == 'True'; return $result; }
protected function _getDistanceBetweenPoints(\Litvinenko\Combinatorics\Pdp\Point $firstPoint, \Litvinenko\Combinatorics\Pdp\Point $secondPoint) { return sqrt(pow($firstPoint->getX() - $secondPoint->getX(), 2) + pow($firstPoint->getY() - $secondPoint->getY(), 2)); }