ガイドAPIリファレンス
ガイド

複数の未割り当てジョブの理由を取得する

デフォルトでは、HERE Tour Planning APIはジョブがどの車両にも割り当てられない場合、フリート内のすべての車両で最も頻繁に発生した制約違反を特定し、その単一の理由コードを返します。ただし、ジョブが車両ごとに異なる理由で割り当てに失敗する可能性があり、理由が1つしか提供されない場合は、根本原因の特定が困難になります。

単一および複数の未割り当ての理由を把握する

デフォルトでは、未割り当てジョブのレスポンスには単一の理由のみが含まれ、通常はすべての車両で最も頻繁に発生する制約違反が示されます。例:

{
    "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
    "reasons": [
        {
            "code": "TERRITORY_CONSTRAINT",
            "description": "cannot be assigned due to territory constraint"
        }
    ]
}

テスト段階の機能multipleUnassignedReasonsを有効にすると、未割り当ての各ジョブについて、車両のシフト単位でグループ化された複数の該当理由コードを取得できます。例:

{
    "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
    "reasons": [
        {
            "code": "TERRITORY_CONSTRAINT",
            "description": "cannot be assigned due to territory constraint",
            "details": [
                {
                    "shiftIndex": 0,
                    "vehicleIds": [
                        "218_1_1",
                        "655_1_1",
                        "695_1_1",
                        "712_1_1"
                    ]
                }
            ]
        },
        {
            "code": "TIME_WINDOW_CONSTRAINT",
            "description": "cannot be assigned due to violation of time window",
            "details": [
                {
                    "shiftIndex": 0,
                    "vehicleIds": [
                        "166_1_1",
                        "180_1_1",
                        "217_1_1"
                    ]
                }
            ]
        }
    ]
}
📘

車両に複数のシフトがあり、ジョブがシフトごとに異なる制約で失敗する場合、同一の車両が複数の理由コードに含まれることがあります。例:

{
 "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
 "reasons": [
   {
     "code": "CAPACITY_CONSTRAINT",
     "description": "cannot be assigned due to capacity of vehicle",
     "details": [
       {
         "shiftIndex": 0,
         "vehicleIds": [
           "218_1_1"
         ]
       }
     ]
   },
   {
     "code": "TIME_WINDOW_CONSTRAINT",
     "description": "cannot be assigned due to violation of time window",
     "details": [
       {
         "shiftIndex": 1,
         "vehicleIds": [
           "218_1_1"
         ]
       }
     ]
   }
 ]
}

この機能により、ジョブを割り当てられなかった理由を包括的に把握でき、問題定義を調整する際に、より適切な判断ができるようになります。

複数の未割り当て理由を有効にする

📘

これはアルファ機能 (新規またはテスト段階であり、現在開発中) です。アルファ機能は、テストおよびフィードバックの目的で提供されています。これらは大幅に変更されたり、一般に入手できなくなったりする可能性があります。

詳細については、「テスト段階の機能の詳細」を参照してください。

ソリューションで複数の未割り当ての理由を受け取るには、次のように問題設定内のexperimentalFeatures配列にmultipleUnassignedReasons機能を追加します。

{
  "configuration": {
    "experimentalFeatures": [
      "multipleUnassignedReasons"
    ]
  },
  "fleet": {
    ...
  },
  "plan": {
    ...
  }
}

multipleUnassignedReasonsを有効にすると、ソリューションには各未割り当てジョブごとにreasons配列が含まれます。単一の理由のみを返すデフォルトの動作とは異なり、この配列には同じジョブに複数の理由コードを含めることができるため、フリート全体でさまざまな制約違反を確認できます。それぞれの理由には次が含まれます。

  • code:制約違反の理由コード (例:SKILL_CONSTRAINTCAPACITY_CONSTRAINT)
  • description:制約違反について読む人が理解できる説明
  • details:この特定の制約の影響を受ける車両とシフトを指定するオブジェクトの配列
📘

重要

複数の未割り当て理由は、ソリューションで実際に使用される車両についてのみ報告されます。

未割り当てジョブについて、サービスは作成されたすべてのツアーを確認し、どのツアーにもジョブを追加できなかった理由を集約します。ツアーが作成されていない車両は対象外となります。利用可能な車両フリートとしてVehicle_1およびVehicle_2が含まれる問題を考えます。

  • ソリューションで1台の車両のみが使用されている場合、その車両に関連する制約 (例:CAPACITY_CONSTRAINTまたはSKILL_CONSTRAINT) のみが表示されます。
  • 複数の未割り当て理由 (例:CAPACITY_CONSTRAINTおよびSKILL_CONSTRAINT、またはCAPACITY_CONSTRAINTおよびREACHABLE_CONSTRAINT) は、ジョブの候補として検討された複数の車両に対してツアーが作成されている場合にのみ表示されます。

例:複雑な制約違反を診断する

この例は、異なる車両がそれぞれ異なる理由で失敗する場合に、複数の未割り当て理由がジョブの割り当て失敗の原因を診断するうえでどのように役立つかを示しています。

問題の概要

フリートの構成は次のとおりです。

  • 1台の車両 (car_1):4ユニットの容量で、大型荷物の輸送に対応するbulky_itemsスキルを持つ
  • スクーター2台 (scooter_1):それぞれが2ユニットの容量で、時間指定の配達に対応するexpress_deliveryスキルを持つ

ジョブは次のように割り当てられています。

  • ジョブ1~3bulky_itemsスキルが必要 (各1ユニット)
  • ジョブ4~8express_deliveryスキルが必要 (各1ユニット)

容量とスキルの制約が厳しいため、Job_8はどの車両にも割り当てられません。

問題

次のセクションには、複数の未割り当て理由を示す完全な問題JSONが含まれています。

Click to expand/collapse the sample JSON
{ "configuration": {
  "experimentalFeatures": [
    "multipleUnassignedReasons"
  ]
},
  "fleet": {
    "types": [
      {
        "id": "car_1",
        "profile": "car",
        "costs": {
          "fixed": 10.0,
          "distance": 0.001,
          "time": 0.002
        },
        "shifts": [
          {
            "start": {
              "time": "2024-06-24T06:00:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            },
            "end": {
              "time": "2024-06-24T06:45:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            }
          }
        ],
        "capacity": [4],
        "skills": ["bulky_items"],
        "amount": 1
      },
      {
        "id": "scooter_1",
        "profile": "scooter",
        "costs": {
          "fixed": 10.0,
          "distance": 0.001,
          "time": 0.002
        },
        "shifts": [
          {
            "start": {
              "time": "2024-06-24T06:00:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            },
            "end": {
              "time": "2024-06-24T06:45:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            }
          }
        ],
        "capacity": [2],
        "skills": ["express_delivery"],
        "amount": 2
      }
    ],
    "profiles": [
      {
        "type": "car",
        "name": "car"
      },
      {
        "type": "scooter",
        "name": "scooter"
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.531757,
                    "lng": 13.384426
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.532983,
                    "lng": 13.38787
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_3",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.532065,
                    "lng": 13.389091
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_4",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530384,
                    "lng": 13.391224
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_5",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530008,
                    "lng": 13.389562
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_6",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.529496,
                    "lng": 13.385547
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_7",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.528909,
                    "lng": 13.385381
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_8",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530533,
                    "lng": 13.383505
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

ソリューションの分析

この問題をmultipleUnassignedReasons機能を有効にして送信すると、最適化アルゴリズムはジョブ1~3を車両に割り当て、ジョブ4~7を2台のスクーターに割り当てます。ただし、Job_8は未割り当てのままになります。ソリューションにより、このジョブをどの車両にも割り当てられない理由が明らかになります。

{
  "unassigned": [
    {
      "jobId": "Job_8",
      "reasons": [
        {
          "code": "CAPACITY_CONSTRAINT",
          "description": "cannot be assigned due to capacity of vehicle",
          "details": [
            {
              "shiftIndex": 0,
              "vehicleIds": [
                "scooter_1_1",
                "scooter_1_2"
              ]
            }
          ]
        },
        {
          "code": "SKILL_CONSTRAINT",
          "description": "cannot be assigned due to required skill",
          "details": [
            {
              "shiftIndex": 0,
              "vehicleIds": [
                "car_1_1"
              ]
            }
          ]
        }
      ]
    }
  ]
}

レスポンスでは次が示されます。

  • スクーター (scooter_1_1およびscooter_1_2) は、ジョブ4~7を処理してすでに2ユニットの容量に達しているため、CAPACITY_CONSTRAINTによりJob_8を引き受けることができない
  • 車両 (car_1_1) はbulky_itemsのスキルを持っているが、Job_8にはexpress_deliveryのスキルが必要なため、SKILL_CONSTRAINTによりJob_8を引き受けることができない

デフォルトの動作と比較する

対照的に、multipleUnassignedReasons機能を有効にせずに同じ問題を送信した場合、サービスは次のように最も頻繁に発生する制約違反のみを返します。

{
  "unassigned": [
    {
      "jobId": "Job_8",
      "reasons": [
        {
          "code": "CAPACITY_CONSTRAINT",
          "description": "cannot be assigned due to capacity of vehicle"
        }
      ]
    }
  ]
}

このデフォルトのレスポンスではCAPACITY_CONSTRAINTのみが示されるため、特定の車両タイプの容量を拡張することが唯一のソリューションであると考えてしまう可能性があります。ただし、全体像を見ると別の問題が明らかになります。車両はSKILL_CONSTRAINTのためこのジョブを引き受けることができません。multipleUnassignedReasons機能を使用しないと、この問題の複数の有効な解決方法を見落とす可能性があります。

解決方法を検討する

複数の未割り当て理由から全体像を把握することで、問題を解決するための選択肢が広がります。例:

  • フリートにスクーターをもう1台追加し、残りのexpress_deliveryジョブを処理する
  • 運用上可能であれば、既存のスクーターの容量を増やして、より多くのジョブに対応できるようにする
  • 車両にexpress_deliveryスキルを付与し、他のジョブと併せてJob_8を処理できるようにする
  • Job_8がどの車両タイプでも処理可能な場合は、スキル要件を削除または調整して、この車両が処理できるようにする
  • スクーターがシフト途中で荷下ろしを行い、その後も追加のジョブを処理できるようにする

複数の未割り当て理由の主な利点は、多数の制約違反を一度に把握できる点にあります。ビジネス目標とフリートの状況を踏まえて、より実行可能なソリューションを選択できます。スクーターの容量制約または車両のスキル制約に対処することで、この問題を解決できます。

次のステップ