Je fais un algorithme de recuit simulé pour optimiser une allocation donnée d'étudiants et de projets.Problème avec la copie de dictionnaires et l'utilisation de deepcopy sur un objet SQLAlchemy ORM
Ce langage est agnostique pseudocode de Wikipedia:
s ← s0; e ← E(s) // Initial state, energy.
sbest ← s; ebest ← e // Initial "best" solution
k ← 0 // Energy evaluation count.
while k < kmax and e > emax // While time left & not good enough:
snew ← neighbour(s) // Pick some neighbour.
enew ← E(snew) // Compute its energy.
if enew < ebest then // Is this a new best?
sbest ← snew; ebest ← enew // Save 'new neighbour' to 'best found'.
if P(e, enew, temp(k/kmax)) > random() then // Should we move to it?
s ← snew; e ← enew // Yes, change state.
k ← k + 1 // One more evaluation done
return sbest // Return the best solution found.
Ce qui suit est une adaptation de la technique. Mon superviseur a dit que l'idée est bonne en théorie. D'abord je prends une certaine allocation (c'est-à-dire un dictionnaire complet des étudiants et leurs projets alloués, y compris les rangs pour les projets) de l'ensemble des allocations aléatoires, copiez-le et passez-le à ma fonction. Appelons cette allocation aOld
(c'est un dictionnaire). aOld
a un poids lié à celui-ci appelé wOld
. La pondération est décrite ci-dessous.
La fonction effectue les opérations suivantes:
- Que cette allocation,
aOld
être lebest_node
- De tous les étudiants, choisir un nombre aléatoire d'étudiants et de s'y tenir dans une liste
- Strip (DEALLOCATE) eux de leurs projets ++ reflètent les changements pour les projets (
allocated
paramètre est maintenantFalse
) et les conférenciers (libérer des emplacements si un ou plusieurs de leurs projets ne sont plus alloués) - Randomiser que la liste
- Essayez d'attribuer (réallouer) tout le monde en ce que les projets de liste à nouveau
- Calculer le poids (additionner les rangs, rang 1 = 1, rang 2 = 2 ... et pas de rang de projet = 101)
- Pour cette nouvelle allocation
aNew
, si le poidswNew
est inférieur au poids d'allocationwOld
que j'ai ramassé au début, alors ceci est lebest_node
(tel que défini par l'algorithmeSimulated Annealing
ci-dessus). Appliquez l'algorithme àaNew
et continuez. - Si
wOld < wNew
, appliquez à nouveau l'algorithme àaOld
et continuez.
Les allocations/points de données sont exprimées en « nœuds » de telle sorte qu'un node = (weight, allocation_dict, projects_dict, lecturers_dict)
En ce moment, je ne peux réaliser cet algorithme une fois, mais je vais devoir essayer un nombre N (noté par kmax
dans l'extrait de Wikipedia) et assurez-vous que j'ai toujours avec moi, le précédent node
et le best_node
.
Afin que je ne modifie pas mes dictionnaires d'origine (que je pourrais vouloir réinitialiser), j'ai fait une copie superficielle des dictionnaires. D'après ce que j'ai lu dans les docs, il semble qu'il copie seulement les références et puisque mes dictionnaires contiennent des objets, changer le dictionnaire copié finit par changer les objets quand même. J'ai donc essayé d'utiliser copy.deepcopy()
. Ces dictionnaires se réfèrent à des objets qui ont été mappés avec SQLA.
Questions:
On m'a donné des solutions aux problèmes rencontrés, mais à cause de mon über vert-ness avec l'utilisation de Python, ils me tous les sons plutôt cryptique.
Deepcopy ne fonctionne pas bien avec SQLA. On m'a dit que la copie sur des objets ORM posait probablement des problèmes qui l'empêchaient de fonctionner comme prévu. Apparemment, je ferais mieux de construire des constructeurs de copie, c'est-à-dire de copier (self): retourner FooBar (....). Quelqu'un peut-il expliquer ce que cela signifie?
J'ai vérifié et découvert que
deepcopy
a des problèmes parce SQLAlchemy place des informations supplémentaires sur vos objets, à savoir un attribut_sa_instance_state
, que je ne voudrais pas que dans la copie mais il est nécessaire que l'objet ait. On m'a dit: "Il y a des façons de souffler manuellement le vieux_sa_instance_state
et d'en mettre un nouveau sur l'objet, mais le plus simple est de faire un nouvel objet avec__init__()
et de configurer les attributs qui sont significatifs, au lieu de le faire une copie complète complète. " Qu'est-ce que cela signifie exactement? Est-ce que je crée une nouvelle classe non mappée similaire à l'ancienne, mappée?Une solution alternative est que je dois « mettre en œuvre
__deepcopy__()
sur vos objets et faire en sorte qu'un nouveau _sa_instance_state est mis en place, il y a des fonctions sqlalchemy.orm.attributes qui peuvent aider. » Encore une fois c'est au-delà de moi, donc quelqu'un pourrait-il expliquer ce que cela signifie?Une question plus générale: étant donné les informations ci-dessus sont-il des suggestions sur la façon dont je peux maintenir l'information/état pour la
best_node
(qui doit toujours persister dans ma boucle while) et leprevious_node
, si mes objets réels (référencé par les dictionnaires, donc les nœuds) changent en raison de la désallocation/réallocation en cours? C'est, sans utiliser de copie?
Cela semble intéressant. Que fait sess.rollback() 'revenir à? L'état original ou précédent? De plus, cela signifie que je n'aurais pas à changer l'une de mes classes? Juste le point où je les commets des changements, n'est-ce pas? – PizzAzzra
sess.rollback() retournera à l'état à partir du dernier appel à sess.commit(). Vos dictionnaires ne sont pas des objets SQLAlchemy, ils ne seront donc pas restaurés. Vous devrez les gérer vous-même. Sinon, vous ne devriez pas avoir besoin de changements supplémentaires. –
Cela ne change pas grand-chose mais la fonction 'rollback()' est très utile car je passe de "state" à "state". – PizzAzzra