- Pemrograman kendala (CP) adalah pendekatan deklaratif untuk memodelkan masalah optimisasi diskret bukan dengan kode prosedural, melainkan dengan variabel, domain, dan kendala, lalu membiarkan solver menemukan solusi yang memenuhi kondisi tersebut
- Inti model adalah variabel sebagai nilai yang ingin dicari, domain sebagai rentang nilai yang mungkin, dan kendala yang membatasi hubungan antarvariabel; bila perlu, fungsi objektif dapat digunakan untuk memilih solusi yang lebih baik
- Contoh pembagian biaya permen antara Alice, Bob, dan Carol menunjukkan alur memperbaiki solusi valid menjadi solusi yang lebih merata melalui
alldifferent,maximum, danminimize - Contoh praktik menyusun jadwal kerja mingguan untuk 4 karyawan, 7 hari, 3 shift, dan 2 peran menggunakan solver open source CP-SAT dari Google OR-Tools dan Python
- Pada model yang sama, kondisi seperti batas 40 jam per minggu, jadwal kuliah, kombinasi orang yang tidak boleh bekerja bersama, pembagian kerja akhir pekan yang merata, permintaan cuti, dan minimisasi selisih jumlah shift dapat ditambahkan secara bertahap
Cara berpikir dasar dalam pemrograman kendala
- Pemrograman kendala (CP) adalah paradigma deklaratif untuk menyelesaikan masalah optimisasi diskret
- Pemrograman imperatif menuliskan prosedur untuk mencapai hasil secara berurutan, sedangkan pendekatan deklaratif mendeskripsikan kondisi hasil yang diinginkan dan membiarkan sistem eksekusi menemukan hasil tersebut
- Dalam contoh mencari daftar orang dewasa, kode imperatif menelusuri daftar orang dan memeriksa
Age >= 18, sedangkan SQL deklaratif mengekspresikan kondisinya secara langsung sepertiSELECT person_name FROM people WHERE age >= 18; - CP juga mendeskripsikan hasil yang diinginkan sebagai model, dengan komponen inti berupa variabel, domain, dan kendala
- Variabel menunjukkan apa yang ingin dicari
- Domain adalah himpunan nilai yang dapat dimiliki variabel
- Kendala membatasi hubungan antarvariabel
Variabel, domain, kendala, dan fungsi objektif
- Solusi adalah penetapan nilai untuk setiap variabel yang berada di dalam domain masing-masing dan memenuhi semua kendala
- Contoh biaya permen adalah masalah Alice, Bob, dan Carol yang masing-masing memiliki maksimal 20 dolar dan mengumpulkan uang untuk membeli permen seharga 50 dolar
- Variabel
a,b,cadalah jumlah uang yang dibayar masing-masing orang - Domain ketiga variabel adalah
{0, ..., 20} a + b + c == 50menyesuaikan totalnyaa >= bmembuat Alice membayar setidaknya sebanyak Bobc % 5 == 0membatasi jumlah Carol ke kelipatan 5- Agar ketiganya tidak membayar jumlah yang sama, dapat diberi
a != b,a != c,b != c
- Variabel
- Kondisi yang melibatkan banyak variabel dapat diekspresikan sebagai kendala global (global constraints), dan
alldifferent(a, b, c)membuat ketiga variabel memiliki nilai yang semuanya berbeda - Solver menerima model sebagai input dan mengembalikan solusi yang valid
- Contoh solusi
a = 19,b = 11,c = 20memenuhi semua kendala - Namun karena Carol membayar hampir dua kali lipat Bob, mungkin ada solusi yang lebih merata
- Contoh solusi
- Fungsi objektif meminimalkan atau memaksimalkan ekspresi tertentu di antara solusi yang memenuhi kendala
- Tetapkan variabel baru
xsebagai kontribusi terbesar dan gunakanmaximum(x, [a, b, c]) - Jika
minimize: xditerapkan, hasilnya adalaha = 18,b = 17,c = 15,x = 18 - Selisih antara kontribusi terbesar dan terkecil berkurang dari 9 dolar menjadi 3 dolar
- Tetapkan variabel baru
Membuat model jadwal kerja dengan CP-SAT dan Python
- Contoh praktik adalah masalah membuat jadwal kerja mingguan untuk toko kecil
- Toko buka setiap hari dari pukul 08.00 hingga 20.00
- Satu hari terdiri dari tiga shift: Morning, Afternoon, Evening, dan setiap shift berdurasi 4 jam
- Ada dua peran: Cashier dan Restocker
- Karyawannya adalah Phil, Emma, David, dan Rebecca
- CP-SAT adalah solver CP open source yang termasuk dalam Google OR-Tools
- Model kosong dibuat dengan
cp_model.CpModel()dariortools.sat.python - Peran yang dapat dilakukan tiap karyawan adalah sebagai berikut
- Phil: Restocker
- Emma: Cashier, Restocker
- David: Cashier, Restocker
- Rebecca: Cashier
- Jadwal kerja direpresentasikan sebagai variabel boolean yang menggabungkan karyawan, peran, hari, dan shift
schedule["Emma"]["Restocker"]["Monday"]["Evening"]bernilai1jika Emma bekerja sebagai Restocker pada shift malam hari Senin, dan0jika tidakmodel.new_bool_var()membuat variabel dengan domain{0, 1}
Kendala dasar jadwal kerja
- Karena diperlukan tepat satu kasir di setiap slot waktu, jumlah peran Cashier untuk setiap hari dan shift harus
1 - Petugas restock hanya diperlukan satu shift per hari, sehingga jumlah total peran Restocker untuk setiap hari ditetapkan
1 - Agar shift restock Evening pada hari sebelumnya tidak langsung berlanjut ke shift restock Morning pada hari berikutnya, jumlah kedua penugasan dibatasi tidak melebihi
1 - Satu karyawan tidak dapat memegang dua peran sekaligus pada shift yang sama, sehingga jumlah peran per karyawan, hari, dan shift harus paling banyak
1 - Agar peran yang tidak memenuhi kualifikasi tidak ditugaskan, semua variabel peran yang tidak dapat dilakukan karyawan tersebut dikunci ke
0 - Jam kerja maksimal per hari adalah 8 jam, yaitu 2 shift
- Jika Morning dan Evening ditugaskan pada hari yang sama, akan muncul waktu menganggur 4 jam selama Afternoon
- Dengan membatasi jumlah penugasan Morning dan Evening per karyawan dan hari menjadi paling banyak
1, model sekaligus mencegah lebih dari 2 shift per hari dan waktu menganggur di tengah
Menjalankan solver dan hasil awal
- Untuk menyelesaikan model, buat
cp_model.CpSolver()dan panggilsolver.solve(model) - Setelah mendapatkan solusi, nilai variabel
scheduledibaca dengansolver.value(...) - Jadwal kerja awal memenuhi semua kendala dasar, tetapi hasilnya Rebecca mendapat 14 shift dalam seminggu
- Untuk menghindari lembur, tambahkan kendala yang membatasi kerja mingguan tiap karyawan hingga maksimal 40 jam, yaitu 10 shift
- Phil adalah mahasiswa penuh waktu, jadi ia hanya bekerja tepat 4 shift per minggu dan tidak dapat bekerja pada Morning dan Afternoon di hari kerja karena kuliah
- Agar Phil dan Emma tidak bekerja pada shift yang sama, jumlah penugasan keduanya dibatasi paling banyak
1untuk setiap hari dan shift - Pekerjaan akhir pekan yang tidak disukai semua orang dibagi ke empat karyawan masing-masing 2 shift dari total 8 shift pada Sabtu dan Minggu
Status solusi: OPTIMAL, INFEASIBLE, FEASIBLE, UNKNOWN
- Solver menerima model dan mengembalikan status serta solusi
OPTIMALberarti solver telah menemukan solusi yang tidak memiliki solusi lain yang lebih baik- Misalnya, saat
x + y >= 5danx + ydiminimalkan,(x, y) = (5, 0)adalah solusi optimal (x, y) = (3, 2)juga memiliki nilai objektif yang sama, sehingga dapat menjadi solusi optimal
- Misalnya, saat
INFEASIBLEberarti tidak ada cara menetapkan nilai ke variabel yang dapat memenuhi kendala- Misalnya, jika
x ∈ {0, ..., 10}tetapi dimintax >= 15, itu tidak mungkin
- Misalnya, jika
- Jika solver dihentikan karena batas waktu akibat masalah besar atau fungsi objektif yang kompleks, ada dua status yang mungkin muncul
FEASIBLE: solusi yang memenuhi kendala telah ditemukan, tetapi belum diketahui apakah optimalUNKNOWN: solusi belum ditemukan, dan belum diketahui apakah solusi ada atau tidak
Permintaan cuti dan pembagian yang adil
- Jika ditambahkan kendala bahwa Emma ingin libur dari Senin hingga Jumat, status solver menjadi INFEASIBLE
- Sebab jadwal tidak dapat diisi tanpa melanggar kendala lain
- Jika kondisinya diubah sehingga Emma hanya libur dari Senin hingga Rabu, jadwal kerja dapat dibuat
- Phil bekerja tepat 4 shift sesuai keinginannya
- Emma mendapat 6 shift, David 10 shift, dan Rebecca 8 shift
- Untuk membuat jumlah shift antara Emma, David, dan Rebecca lebih merata, tambahkan fungsi objektif
- Buat variabel integer
total_shiftsyang menunjukkan total jumlah shift setiap karyawan model.new_int_var(0, 10, ...)membuat variabel integer dengan nilai dari 0 hingga 10- Karena Phil paruh waktu, ia dikecualikan, lalu jumlah shift minimum dan maksimum dilacak dengan
model.add_min_equality(...)danmodel.add_max_equality(...) model.minimize(max_shifts - min_shifts)meminimalkan selisih antara jumlah shift maksimum dan minimum
- Buat variabel integer
- Hasil akhir adalah Phil 4 shift, Emma 6 shift, David 9 shift, dan Rebecca 9 shift
- Karena Emma libur 3 hari, ia mendapat 6 shift
- David dan Rebecca dibagi sama rata, masing-masing 9 shift
Kode contoh dan topik berikutnya
- Model ini membuat jadwal kerja yang sekaligus memenuhi permintaan pemilik toko dan kebutuhan karyawan
- Dengan terus menambahkan kendala ke model CP yang sama, kita dapat memeriksa apakah permintaan memungkinkan, lalu mencari pembagian yang lebih adil di antara solusi yang memungkinkan menggunakan fungsi objektif
- Kode contoh tersedia di pganalyze GitHub
- Topik artikel berikutnya adalah cara menggunakan pemrograman kendala untuk pemilihan indeks di Postgres
1 komentar
Opini Hacker News
Dulu saya pernah mencoba constraint solver, dan hal-hal yang bisa dilakukannya terasa benar-benar seperti sulap. Masalahnya, tidak banyak materi yang cocok untuk pemula
Kebanyakan hanya tentang memecahkan Sudoku (Hello World di bidang ini), atau literatur riset primer yang sangat teknis dan hanya ditujukan untuk pakar domain
Sayangnya, kalau alat seperti ini menjadi lebih mudah diakses, rasanya akan ada sangat banyak masalah yang bisa diselesaikan. Yang dimaksud mudah diakses di sini pun tetap berarti masih membutuhkan programmer, dan membentuk sebuah masalah ke dalam DSL constraint bukanlah bidang yang bisa dilakukan dengan baik oleh kebanyakan orang
Namun MIP bukan satu-satunya jenis solver. Ada juga constraint solver berbasis local search, dan pendekatan ini tidak dibatasi oleh keharusan memodelkan semua constraint sebagai relasi atau persamaan antar variabel integer
Dalam solver local search, constraint umumnya diperlakukan sebagai black box yang memberi tahu seberapa baik sebuah solusi tertentu. Karena itu, sulit menjamin solusi optimal kecuali mencoba semua kemungkinan solusi, tetapi biasanya bisa menemukan solusi suboptimal dalam waktu yang masuk akal
Timefold Solver adalah salah satu solver berbasis local search seperti ini. Pengguna memberi anotasi pada domain agar solver dapat mengetahui variabel dan nilai-nilai yang mungkin. Jadi constraint menangani
ShiftdanEmployee, bukanint, dan juga bisa mengakses metodenyaPengungkapan: saya bekerja di Timefold Solver
Saya punya pengalaman sekitar 5 tahun menyelesaikan masalah penjadwalan dengan MiniZinc, tetapi sayangnya semua kode itu tertutup sehingga tidak akan dirilis sebagai open source
Saya ingin membuat contoh constraint programming yang lengkap, termasuk containerization, visualisasi, dan modeling, tetapi hambatannya adalah menemukan masalah yang benar-benar layak diselesaikan dan memiliki data open source yang bisa digunakan
Setelah cukup banyak menebak-nebak, saya berhasil membuat proof of concept dasar, tetapi tidak bisa menskalakannya sampai tingkat yang benar-benar dibutuhkan. Kesenjangan antara implementasi mainan dan sesuatu yang lebih substantif sangat besar
LLM membantu membawa saya cukup cepat ke arah yang kira-kira benar. Saat ini memang gagal mendapatkan hasil yang benar-benar tepat, tetapi cukup membantu sehingga saya bisa menyelesaikan sisanya sendiri
Inti dari semua ini adalah belajar cara memodelkan sesuatu dalam bentuk yang bisa dikirim ke solver. Setelah itu, bagaimana menyajikan solusi yang dihasilkan agar bisa dipahami manusia
Sayangnya, sebagian besar program mencoba mempertahankan data dalam satu representasi saja, yang berlawanan dengan cara berpikir ini. Dalam kebanyakan kasus, itu tidak masuk akal, dan banyak kerumitan muncul karena harus menyesuaikan algoritme dengan representasi baru
Tulisan ini juga menyinggung hal tersebut di bagian awal dengan sedikit membahas gaya deklaratif. Saya selalu menyesal kode saya tidak lebih sering melakukan konversi antar representasi. Dengan begitu kita bisa mendapat representasi yang sangat ringkas, dan karena menjadi ringkas, sering kali juga mendapatkan keuntungan ganda berupa performa yang lebih cepat
Tentu saya sadar bahwa pada akhirnya ini juga menggambarkan banyak data pipeline: struktur yang menghabiskan sebagian besar waktunya untuk mentransformasi data dan mencabangkannya ke berbagai lokasi komputasi
Di salah satu buku lama saya yang kini sedang saya tulis ulang, ada bab pendek tentang penggunaan MiniZinc di Python: https://leanpub.com/pythonai/read#constraint-programming-wit...
MiniZinc adalah sistem constraint programming. Ada juga kursus Coursera yang bagus yang menggunakan MiniZinc
Setelah belajar ekonometrika, saya banyak memakai solver saat mengambil master riset operasi pada awal 2000-an. Sekarang saya bekerja di perangkat lunak web dengan Python, dan senang melihat tulisan mendalam tentang topik ini
Saya menyukai topik ini, dan membaca tulisan tersebut membangkitkan banyak kenangan. Saya juga kembali menyadari bahwa memindahkan constraint ke dalam model (variabel, struktur, dan sebagainya) adalah 90% pekerjaan sekaligus bagian tersulit
Struktur sintaksnya benar-benar berformat bebas
https://www.gams.com/latest/docs/UG_GAMSPrograms.html#UG_GAM...
Kelihatannya seperti buah yang cukup mudah dipetik, tetapi saya penasaran apakah orang lain juga akan mendapat manfaatnya
Ada klien yang menjalankan kamp olahraga anak-anak. Anak-anak bisa meminta olahraga yang ingin mereka ikuti dan teman yang ingin mereka satu kelompok dengannya.
Karena itu muncul masalah penjadwalan yang sulit diselesaikan manusia, dan dulu setiap tahun pekerjaan ini memakan tenaga selama beberapa minggu. Saya membuatkan sistem sederhana yang menghubungkan data klien ke optimizer berbasis OR-Tools, dan sekarang penjadwalan selesai hanya dengan beberapa klik.
Saya melatih liga basket dan ada 8 periode. Tidak ada pemain yang boleh bermain 2 periode lebih banyak daripada pemain lain. Jumlah lineup yang mungkin per pertandingan tetap astronomis meski harus memenuhi constraint waktu bermain.
Menemukan kumpulan lineup yang memenuhi constraint sangat mudah, tetapi menemukan kumpulan lineup yang optimal atau mendekati optimal sangat sulit. Akan lebih menarik lagi kalau harus memperhitungkan pemain yang datang terlambat atau absen tanpa kabar.
*Tidak selalu sepenuhnya memungkinkan
Saya penasaran apakah ada CAD parametrik yang terutama bekerja sebagai constraint solver.
Terlalu sering saya terganggu karena harus menebak secara kasar nilai parameter yang awalnya tidak saya pedulikan. Akan bagus kalau parameter yang saya minati bisa dijadikan constraint, lalu sisanya dioptimalkan.
Saya penasaran bagaimana pendekatan ini dibandingkan dengan mixed-integer programming. Bagaimana kalau untuk masalah fisika?
Karena Gurobi luar biasa cepat, kadang layak memaksakan masalah agar masuk ke bentuk MILP demi mendapatkan solusi.
Keunggulan CP-SAT adalah menangani variabel dan constraint Boolean serta integer jauh lebih efisien daripada MIP solver, terutama pada constraint tingkat tinggi seperti
all_different.Khususnya bagian dalam tulisan ini yang mencoba meminimalkan suatu nilai menurut saya pada dasarnya menuliskan hal yang sama secara langsung.