Meta ha publicado como código abierto Rebalancer, una biblioteca de C++ con interfaz de Python para resolver problemas de asignación. Decide qué objetos van en qué contenedores bajo restricciones y objetivos. Según la publicación de Engineering at Meta, Rebalancer ha gestionado la asignación de recursos en Meta durante más de 9 años. La versión se publica bajo Apache 2.0 con documentación, un paquete en PyPI y una interfaz de depuración llamada Rebalancer Explorer.
¿Es desplegable? Sí. pip install rebalancer instala la v1.0.4 para Python 3.12+, con wheels precompilados para Linux x86-64 y macOS 14+ ARM64. También existen paquetes .deb, .rpm y Homebrew. PyPI todavía clasifica el proyecto como Alpha.
¿Qué problema resuelve Rebalancer?
Los problemas de asignación aparecen en toda la pila de Meta. Los racks van a centros de datos, los servidores van a servicios, las tareas van a servidores y el tráfico de usuarios va a centros de datos. Meta señala 2 obstáculos: usabilidad y escalabilidad. A los ingenieros les cuesta convertir políticas en fórmulas precisas, y muchos problemas son NP-difíciles y demasiado grandes para los solucionadores comerciales.
La respuesta de Rebalancer es separar cómo se especifica un problema de cómo se resuelve. El diseño se detalla en el artículo de OSDI 2024, Optimizing Resource Allocation in Hyperscale Datacenters.
Cómo funciona la capa de especificación
El lenguaje de especificación tiene 3 capas:
- Constructos de modelado: dimensiones (atributos como CPU o almacenamiento), particiones (grupos de objetos), scopes (grupos de contenedores) y utilización.
- API de expresiones: agregar la utilización con SUM o MAX, o transformarla con operaciones como SQUARE.
- API de especificación: docenas de objetivos y restricciones predefinidos, listados en la documentación.
El ejemplo de Meta modela las tareas como objetos, los servidores como contenedores y los racks como un scope. Una CapacitySpec limita la CPU y el almacenamiento por servidor. Una GroupCountSpec mantiene 1 tipo de trabajo por rack. Una BalanceSpec equilibra la utilización de cada servidor en ambas dimensiones.
Un grafo de expresiones, dos solvers
Rebalancer compila la especificación en un grafo de expresiones acíclico dirigido. Los nodos hoja contienen valores de utilización; los nodos de agregación y transformación se sitúan por encima. Los usuarios proporcionan una asignación inicial y una condición de parada. Las restricciones que la asignación inicial ya viola se convierten en objetivos de alta prioridad.
Solver óptimo: El grafo se traduce en un programa de enteros mixtos para FICO Xpress, Gurobi o HiGHS. La agregación de variables y la ruptura de simetrías reducen los modelos. El tamaño del modelo en el peor de los casos sigue siendo O(objetos × bins). Los problemas más grandes de Meta son demasiado grandes para cualquier solver MIP.
Búsqueda local: Este solver trabaja directamente sobre el grafo de expresiones. Explora movimientos de objetos a otros bins, con una vecindad en el peor de los casos de O(objetos + bins). Luego aplica el mejor candidato que no viola ninguna restricción. La evaluación se paraleliza, alcanzando millones de evaluaciones por segundo, y el espacio de búsqueda se recorta.
Meta utiliza búsqueda local para casi todos los problemas grandes y MIP para los pequeños y medianos, a menudo prototipando primero con MIP.
Cifras de producción en Meta
- Aproximadamente 40 millones de problemas de asignación resueltos por día, en más de 30 formulaciones únicas.
- Tiempo de resolución P99 de 12 segundos en 265k objetos y 3.2k bins.
- Problemas de más de 1 millón de objetos y 5k bins promedian 171 segundos, en más de 3.4k ejecuciones.
Mejores casos de uso para Rebalancer
- Colocación de shards, tareas o contenedores en un clúster: Asignar trabajo a servidores bajo límites de CPU y memoria mientras se distribuyen las réplicas entre racks. El Shard Manager y RAS de Meta ejecutan este patrón.
- Equilibrio de tráfico y cargas de trabajo entre regiones: Enrutar el tráfico de usuarios o los trabajos a centros de datos, compensando la latencia frente a la carga. Taiji hace esto para el tráfico de borde, y Meta equilibra el entrenamiento de ML por prioridad.
- Asignación operativa fuera de la infraestructura: Asigna tickets de soporte a ingenieros, reuniones a salas o escritorios a personas bajo reglas de capacidad. Meta ha hecho las 3 cosas.
Depuración con Rebalancer Explorer
Los modeladores de Meta pasaban la mayor parte de su tiempo depurando el comportamiento del solver. Rebalancer Explorer es una interfaz web en Docker construida para esto. Muestra las restricciones de enlace (binding constraints), los efectos de la relajación y por qué un objeto cayó en un contenedor.
Explicación interactiva
Ejecutar búsqueda local</button> <button class="b" id="mtpStep">Avanzar un paso</button> <button class="b" id="mtpShuf">Inicio aleatorio malo</button> <button class="b" id="mtpReset">Reiniciar</button> </div> <div class="stats"> <div class="st"><div class="k">Paso</div><div class="v" id="mtpS">0</div></div> <div class="st"><div class="k">Movimientos evaluados</div><div class="v" id="mtpE">0</div></div> <div class="st"><div class="k">Desbordamiento de capacidad</div><div class="v" id="mtpV">0</div></div> <div class="st"><div class="k">Objetivo de equilibrio</div><div class="v" id="mtpO">0</div></div> </div> <div class="racks" id="mtpRacks"></div> <div class="spark"><div class="k">Objetivo a lo largo de los pasos (menor es mejor, ideal = 324)</div> <svg id="mtpSpark" viewBox="0 0 800 70" width="100%" height="70" preserveAspectRatio="none"></svg> </div> <div class="log" id="mtpLog">El servidor S1 comienza con 4 CPU por encima de su capacidad. Pulsa Ejecutar.</div> </div> <!-- PANE 2 --> <div class="pane" id="mtpP1"> <p class="note">Rebalancer compila las especificaciones en un <b>grafo de expresiones acíclico dirigido</b>. Las hojas contienen la utilización de cada servidor; los nodos SQUARE, SUM y MAX se sitúan por encima de ellas. Cuando se mueve una tarea, solo las hojas que toca y sus ancestros necesitan nuevos valores. El grafo usa el mismo estado en vivo que la pestaña 1.</p> <div class="btns"> <button class="b pri" id="mtpGMove">Mover una tarea aleatoria</button> <button class="b" id="mtpGBest">Aplicar el mejor movimiento de búsqueda local</button> </div> <div class="gwrap"><svg id="mtpGraph" viewBox="0 0 820 300" width="100%"></svg></div> <div class="log" id="mtpGLog">Pulsa un botón para mover una tarea.</div> </div> <!-- PANE 3 --> <div class="pane" id="mtpP2"> <p class="note">El modelo MIP necesita aproximadamente una variable binaria por objeto por contenedor, así que crece como <b>O(objetos × contenedores)</b>. Un vecindario de búsqueda local crece como <b>O(objetos + contenedores)</b>. Arrastra los deslizadores o carga los tamaños publicados por Meta.</p> <div class="btns"> <button class="b" data-o="500" data-bn="20">Pequeño: 500 × 20</button> <button class="b" data-o="265000" data-bn="3200">Meta P99: 265k × 3.2k</button> <button class="b" data-o="1000000" data-bn="5000">Meta XL: 1M × 5k</button> </div> <div class="sl"><label>Objetos <b id="mtpOv"></b></label><input type="range" id="mtpOs" min="1" max="6.3" step="0.01" value="2.7"></div> <div class="sl"><label>Contenedores <b id="mtpBv"></b></label><input type="range" id="mtpBs" min="0.3" max="4" step="0.01" value="1.3"></div> <div class="cmp"> <div class="row"><div class="t">Variables binarias del MIP, peor caso <span id="mtpMv"></span></div><div class="track"><i id="mtpMb" style="background:linear-gradient(90deg,#FF8A5A,#FF5A6E)"></i></div></div> <div class="row"><div class="t">Vecindario de búsqueda local, peor caso <span id="mtpLv"></span></div><div class="track"><i id="mtpLb" style="background:linear-gradient(90deg,#0866FF,#38D6FF)"></i></div></div> </div> <div class="verdict" id="mtpVerdict"></div> <p class="note" style="margin-top:10px">Las barras usan una escala logarítmica. Los rangos de tamaño en el veredicto son ilustrativos; la regla declarada por Meta es búsqueda local para casi todos los problemas grandes y MIP para los pequeños y medianos.</p> </div> <!-- PANE 4 --> <div class="pane" id="mtpP3"> <p class="note">Cifras de producción publicadas por Meta para Rebalancer (Engineering at Meta, 21 de sep de 2026).</p> <div class="grid" id="mtpCards"> <div class="card"><div class="n"><span data-c="40">0</span><em>M</em></div><div class="d">problemas de asignación resueltos por día</div></div> <div class="card"><div class="n"><span data-c="30">0</span><em>+</em></div><div class="d">formulaciones únicas de problemas</div></div> <div class="card"><div class="n"><span data-c="12">0</span><em>s</em></div><div class="d">tiempo de resolución P99 con 265k objetos y 3.2k bins</div></div> <div class="card"><div class="n"><span data-c="171">0</span><em>s</em></div><div class="d">resolución promedio para 1M+ objetos y 5k bins</div></div> <div class="card"><div class="n"><span data-c="3.4" data-d="1">0</span><em>k+</em></div><div class="d">ejecuciones a esa escala de 1M+ objetos</div></div> <div class="card"><div class="n"><span data-c="9">0</span><em>+ yrs</em></div><div class="d">en uso en toda Meta antes de liberar el código</div></div> </div> <div class="pills"> <span class="pill">Shard Manager: shards → servidores</span> <span class="pill">RAS: servidores → servicios</span> <span class="pill">Taiji: tráfico de borde → centros de datos</span> <span class="pill">Agrupación de funciones serverless</span> <span class="pill">Equilibrio de entrenamiento de ML</span> <span class="pill">Reuniones → salas</span> </div> </div> <div class="ft"> <span>Fuentes: <a href="https://engineering.fb.com/2026/09/21/open-source/rebalancer-generic-high-performance-library-assignment-problems/" target="_blank" rel="noopener">Engineering at Meta</a> · <a href="https://github.com/facebook/rebalancer" target="_blank" rel="noopener">GitHub</a> · Las pestañas 1 a 3 son simulaciones simplificadas</span> <span class="brand">Creado por Marktechpost</span> </div> </div> <script> (function(){ var R=document.getElementById('mtp-rebal'); function postH(){try{parent.postMessage({mtpRebalH:R.offsetHeight+40},'*');}catch(e){}} var SIZES=[6,5,4,4,3,3,3,2,2,2,1,1], CAP=16, NS=4; var START=[0,0,0,0,1,1,1,2,2,2,0,1]; var asg=START.slice(), step=0, evals=0, hist=[], timer=null, hot=-1; function util(a){var u=[0,0,0,0];for(var i=0;i<a.length;i++)u[a[i]]+=SIZES[i];return u;} function score(a){var u=util(a),v=0,o=0;for(var s=0;s<NS;s++){v+=Math.max(0,u[s]-CAP);o+=u[s]*u[s];}return [v,o];} function better(x,y){return x[0]<y[0]||(x[0]===y[0]&&x[1]<y[1]);} function bestMove(){ var cur=score(asg),best=null,bs=cur,n=0; for(var i=0;i<asg.length;i++){for(var s=0;s<NS;s++){if(s===asg[i])continue;var b=asg.slice();b[i]=s;n++;var sc=score(b);if(better(sc,bs)){bs=sc;best={a:b,txt:'move t'+(i+1)+' S'+(asg[i]+1)+' → S'+(s+1),ids:[i]};}}} for(var i2=0;i2<asg.length;i2++)for(var j=i2+1;j<asg.length;j++){if(asg[i2]===asg[j])continue;var c=asg.slice();c[i2]=asg[j];c[j]=asg[i2];n++;var sc2=score(c);if(better(sc2,bs)){bs=sc2;best={a:c,txt:'swap t'+(i2+1)+' t'+(j+1),ids:[i2,j]};}} return {best:best,n:n,sc:bs}; } var racksEl=document.getElementById('mtpRacks'); function rects(){var m={};racksEl.querySelectorAll('.chip').forEach(function(c){m[c.dataset.id]=c.getBoundingClientRect();});return m;} function render(ids){ var before=rects(),u=util(asg),h=''; for(var r=0;r<2;r++){h+='<div class="rack"><div class="rl">Rack '+(r?'B':'A')+' (alcance)</div><div class="srvs">'; for(var s=r*2;s<r*2+2;s++){var over=u[s]>CAP; h+='<div class="srv'+(over?' over':'')+'"><div class="sn">S'+(s+1)+'<span>'+u[s]+' / '+CAP+' CPU</span></div><div class="bar"><i style="width:'+Math.min(100,u[s]/24*100)+'%"></i><u style="left:'+(CAP/24*100)+'%"></u></div><div class="chips">'; for(var i=0;i<asg.length;i++)if(asg[i]===s){h+='<div class="chip'+(ids&&ids.indexOf(i)>-1?' hot':'')+'" data-id="'+i+'" style="width:'+(30+SIZES[i]*8)+'px">t'+(i+1)+'·'+SIZES[i]+'</div>';} h+='</div></div>';} h+='</div></div>';} racksEl.innerHTML=h; racksEl.querySelectorAll('.chip').forEach(function(c){var b=before[c.dataset.id];if(!b)return;var a=c.getBoundingClientRect(),dx=b.left-a.left,dy=b.top-a.top;if(dx||dy){c.style.transition='none';c.style.transform='translate('+dx+'px,'+dy+'px)';requestAnimationFrame(function(){requestAnimationFrame(function(){c.style.transition='transform .55s cubic-bezier(.2,.8,.2,1)';c.style.transform='';});});}}); var sc=score(asg); document.getElementById('mtpS').textContent=step; document.getElementById('mtpE').textContent=evals; var V=document.getElementById('mtpV');V.textContent=sc[0];V.className='v '+(sc[0]?'bad':'good'); var O=document.getElementById('mtpO');O.textContent=sc[1];O.className='v'+(sc[1]===324?' good':''); spark();drawGraph(null);postH(); } function spark(){ var s=document.getElementById('mtpSpark'),pts=hist.length?hist:[score(asg)[1]]; var mx=Math.max.apply(null,pts.concat([600])),mn=300,n=Math.max(pts.length-1,12); var p=pts.map(function(v,i){return (i/n*790+5)+','+(65-(v-mn)/(mx-mn)*58);}).join(' '); var ideal=65-(324-mn)/(mx-mn)*58; s.innerHTML='<line x1="0" x2="800" y1="'+ideal+'" y2="'+ideal+'" stroke="#2BD99F" stroke-dasharray="4 4" opacity=".6"/><polyline points="'+p+'" fill="none" stroke="#38D6FF" stroke-width="2.5"/>'+pts.map(function(v,i){return '<circle cx="'+(i/n*790+5)+'" cy="'+(65-(v-mn)/(mx-mn)*58)+'" r="3.5" fill="#0866FF" stroke="#fff" stroke-width="1"/>';}).join(''); } var logEl=document.getElementById('mtpLog'); function doStep(){ var r=bestMove();evals+=r.n; if(!r.best){stop();logEl.textContent='Óptimo local: ningún movimiento o intercambio mejora la puntuación ('+evals+' evaluaciones).';render();return false;} var old=asg;asg=r.best.a;step++;hist.push(score(asg)[1]); var moved=r.best.ids;logEl.textContent='Paso '+step+': '+r.best.txt+' (comprobados '+r.n+' candidatos)'; render(moved);drawGraph(diff(old,asg));return true; } function diff(a,b){var s={};for(var i=0;i<a.length;i++)if(a[i]!==b[i]){s[a[i]]=1;s[b[i]]=1;}return Object.keys(s).map(Number);} function stop(){if(timer){clearInterval(timer);timer=null;}document.getElementById('mtpRun').textContent=' Run local search';} document.getElementById('mtpRun').onclick=function(){if(timer){stop();return;}this.textContent='❚❚ Pausa';if(!doStep())return;timer=setInterval(function(){if(!doStep())stop();},900);}; document.getElementById('mtpStep').onclick=function(){stop();doStep();}; function resetTo(a,msg){stop();asg=a;step=0;evals=0;hist=[score(asg)[1]];logEl.textContent=msg;render();} document.getElementById('mtpReset').onclick=function(){resetTo(START.slice(),'El servidor S1 empieza con 4 CPU sobrecargadas. Pulsa Run.');}; document.getElementById('mtpShuf').onclick=function(){var a=[];for(var i=0;i<12;i++)a.push(Math.random()<.55?Math.floor(Math.random()*2):Math.floor(Math.random()*4));resetTo(a,'Nuevo inicio aleatorio. Pulsa Run.');}; /* expression graph */ var G=document.getElementById('mtpGraph'),gLog=document.getElementById('mtpGLog'); var LX=[110,300,490,680]; function drawGraph(live){ live=live||[];var u=util(asg),sc=score(asg),h=''; function on(s){return live.indexOf(s)>-1;} var any=live.length>0; for(var s=0;s<4;s++){ h+='<path class="edge'+(on(s)?' live':'')+'" d="M'+LX[s]+',232 L'+LX[s]+',172"/>'; h+='<path class="edge'+(on(s)?' live':'')+'" d="M'+LX[s]+',138 C'+LX[s]+',100 300,110 300,78"/>'; h+='<path class="edge'+(on(s)?' live':'')+'" d="M'+(LX[s]+40)+',233 C'+(LX[s]+60)+',200 650,130 650,78"/>'; } h+='<path class="edge'+(any?' live':'')+'" d="M300,44 L300,22"/><path class="edge'+(any?' live':'')+'" d="M650,44 L650,22"/>'; function node(x,y,w,label,val,l){return '<g class="node'+(l?' live':'')+'"><rect x="'+(x-w/2)+'" y="'+(y-17)+'" width="'+w+'" height="34" rx="8"/><text x="'+x+'" y="'+(y-2)+'">'+label+'</text><text class="val" x="'+x+'" y="'+(y+11)+'">'+val+'</text></g>';} for(var k=0;k<4;k++){h+=node(LX[k],250,96,'U(S'+(k+1)+')','= '+u[k],on(k));h+=node(LX[k],155,96,'SQUARE','= '+u[k]*u[k],on(k));} h+=node(300,61,120,'SUM','= '+sc[1],any);h+=node(650,61,120,'MAX','= '+Math.max.apply(null,u),any); h+='<text x="300" y="14" fill="#4C9BFF" font-size="11" font-weight="700" text-anchor="middle">Objetivo de BalanceSpec</text>'; h+='<text x="650" y="14" fill="'+(Math.max.apply(null,u)>CAP?'#FF5A6E':'#2BD99F')+'" font-size="11" font-weight="700" text-anchor="middle">CapacitySpec: MAX ≤ '+CAP+(Math.max.apply(null,u)>CAP?' (violado)':' (ok)')+'</text>'; h+='<text x="410" y="292" fill="#93A3BF" font-size="11" text-anchor="middle">Hojas: utilización por servidor · nodos recalculados en este movimiento: '+(any?(live.length*2+2):0)+' de 10</text>'; G.innerHTML=h; } document.getElementById('mtpGMove').onclick=function(){stop();var i=Math.floor(Math.random()*12),s;do{s=Math.floor(Math.random()*4);}while(s===asg[i]);var old=asg;asg=asg.slice();asg[i]=s;evals++;step++;hist.push(score(asg)[1]);render([i]);var d=diff(old,asg);drawGraph(d);gLog.textContent='Movido t'+(i+1)+' S'+(old[i]+1)+' → S'+(s+1)+'. Solo '+(d.length*2+2)+' de 10 nodos necesitaron nuevos valores.'}; document.getElementById('mtpGBest').onclick=function(){stop();var r=bestMove();evals+=r.n;if(!r.best){gLog.textContent='Óptimo local alcanzado. Prueba primero un movimiento aleatorio.';render();return;}var old=asg;asg=r.best.a;step++;hist.push(score(asg)[1]);render(r.best.ids);var d=diff(old,asg);drawGraph(d);gLog.textContent='El mejor de '+r.n+' candidatos: '+r.best.txt+'. Recalculados '+(d.length*2+2)+' de 10 nodos.'}; /* selector de solver */ var Os=document.getElementById('mtpOs'),Bs=document.getElementById('mtpBs'); function fmt(n){if(n>=1e9)return (n/1e9).toFixed(n>=1e10?0:1)+'B';if(n>=1e6)return (n/1e6).toFixed(n>=1e7?0:1)+'M';if(n>=1e3)return (n/1e3).toFixed(n>=1e4?0:1)+'k';return Math.round(n)+'';} function pick(){ var o=exactO||Math.round(Math.pow(10,+Os.value)),b=exactB||Math.round(Math.pow(10,+Bs.value)),m=o*b,l=o+b;exactO=exactB=0; document.getElementById('mtpOv').textContent=fmt(o);document.getElementById('mtpBv').textContent=fmt(b); document.getElementById('mtpMv').textContent='≈ '+fmt(m);document.getElementById('mtpLv').textContent='≈ '+fmt(l); document.getElementById('mtpMb').style.width=Math.min(100,Math.log10(m)/11*100)+'%'; document.getElementById('mtpLb').style.width=Math.min(100,Math.log10(l)/11*100)+'%'; var v=document.getElementById('mtpVerdict'),t; if(m<=1e6){t='<b>El solver óptimo (MIP) es un buen punto de partida.</b> Modelo pequeño: pásalo a HiGHS, Gurobi o FICO Xpress y obtén una asignación demostrablemente óptima.';v.style.borderColor='#2BD99F';} else if(m<=1e8){t='<b>Prototipa con MIP y luego pasa a búsqueda local.</b> Meta dice que este es un camino habitual: encuentra una base sólida con el solver óptimo y luego migra.';v.style.borderColor='#4C9BFF';} else{t='<b>Búsqueda local.</b> El MIP necesitaría ≈ '+fmt(m)+' variables binarias en el peor caso, mientras que cada vecindad de búsqueda local permanece cerca de '+fmt(l)+'. Meta ejecuta casi todos los problemas grandes de esta manera.';v.style.borderColor='#38D6FF';} v.innerHTML=t;postH(); } var exactO=0,exactB=0;Os.oninput=pick;Bs.oninput=pick; document.querySelectorAll('#mtpP2 button[data-o]').forEach(function(btn){btn.onclick=function(){exactO=+btn.dataset.o;exactB=+btn.dataset.bn;Os.value=Math.log10(+btn.dataset.o);Bs.value=Math.log10(+btn.dataset.bn);pick();};}); /* stats */ var counted=false; function count(){ var cards=document.querySelectorAll('#mtpCards .card'); cards.forEach(function(c,i){c.classList.remove('in');setTimeout(function(){c.classList.add('in');},90*i);}); document.querySelectorAll('#mtpCards [data-c]').forEach(function(el){var tgt=+el.dataset.c,dp=+(el.dataset.d||0),t0=null; function f(ts){if(!t0)t0=ts;var p=Math.min(1,(ts-t0)/1100),e=1-Math.pow(1-p,3);el.textContent=(tgt*e).toFixed(dp);if(p<1)requestAnimationFrame(f);}requestAnimationFrame(f);}); } /* tabs */ var tabs=R.querySelectorAll('.tab'); tabs.forEach(function(t){t.onclick=function(){tabs.forEach(function(x){x.classList.remove('on');});t.classList.add('on'); R.querySelectorAll('.pane').forEach(function(p){p.classList.remove('on');});document.getElementById('mtpP'+t.dataset.p).classList.add('on'); if(t.dataset.p==='3')count();if(t.dataset.p==='1')drawGraph(null);setTimeout(postH,60);setTimeout(postH,450);};}); hist=[score(asg)[1]];render();pick(); window.addEventListener('load',postH);window.addEventListener('resize',postH);setTimeout(postH,300); })(); </script> </body></html>">Rebalancer de Meta frente a las alternativas de código abierto más cercanas
| Característica | Rebalancer de Meta | Google OR-Tools | Timefold Solver (Community) |
|---|---|---|---|
| Licencia | Apache 2.0 | Apache 2.0 | Apache 2.0 (La edición Enterprise es comercial) |
| Lenguaje principal | C++ | C++ | Java |
| APIs | C++, Python | C++, Python, Java, C# | Java, Kotlin |
| Enfoque | Asignación genérica (objetos a bins) | Suite amplia: wrappers de CP-SAT, LP, MIP, enrutamiento, empaquetado, asignación | Planificación: enrutamiento, cuadrantes de personal, programación de horarios, asignación de tareas |
| Búsqueda local | Sí, paralela, sobre el grafo de expresiones | Sí, en el solver de enrutamiento (búsqueda local guiada, recocido simulado, tabú) | Sí, motor central (tabú, recocido simulado, aceptación tardía) |
| Backends MIP | FICO Xpress, Gurobi, HiGHS | Wrappers para solvers MIP comerciales y de código abierto | No utilizado |
| Depuración de la interfaz de usuario | Rebalancer Explorer (Docker) | No listado en el README | Benchmarker; análisis de puntuación en las ediciones comerciales |
| Instalar | pip install rebalancer | pip install ortools | Maven, JDK 21+ |
OR-Tools cubre más clases de problemas, y Timefold se enfoca en la programación y el enrutamiento en la JVM. La ventaja de Rebalancer es una única especificación de asignación que funciona tanto con búsqueda local como con MIP.
Conclusiones clave
- Rebalancer modela cualquier problema de asignación como objetos, contenedores, restricciones y objetivos.
- Las especificaciones se compilan en un grafo de expresiones resuelto mediante búsqueda local o un solver MIP.
- Los backends MIP incluyen FICO Xpress, Gurobi y el HiGHS de código abierto.
- Meta ejecuta alrededor de 40M de problemas al día; el P99 es de 12 s con 265k objetos y 3.2k contenedores.
- Apache 2.0, APIs de C++ y Python, instalable desde PyPI hoy mismo.
Echa un vistazo al Paper, Repositorio de GitHub y Detalles técnicos. Todo el crédito corresponde al investigador de este proyecto. Además, no dudes en seguirnos en Twitter y no olvides unirte a nuestro SubReddit de 150k+ML y suscribirte a nuestro boletín. ¡Espera! ¿estás en telegram? ahora también puedes unirte a nosotros en telegram.
La entrada Meta AI publica Rebalancer como código abierto: un solucionador de problemas de asignación en C++ que ejecuta alrededor de 40 millones de problemas de colocación al día apareció primero en MarkTechPost.