2010-06-03 5 views
0

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 le best_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 maintenant False) 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 poids wNew est inférieur au poids d'allocation wOld que j'ai ramassé au début, alors ceci est le best_node (tel que défini par l'algorithme Simulated 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.

  1. 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?

  2. 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?

  3. 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?

  4. 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 le previous_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?

Répondre

2

J'ai une autre solution possible: utiliser des transactions. Ce n'est probablement pas la meilleure solution, mais l'implémenter devrait être plus rapide.

créer d'abord votre session comme ceci:

# transactional session 
Session = sessionmaker(transactional=True) 
sess = Session() 

De cette façon, il sera transactionnel. Le fonctionnement des transactions est que sess.commit() rendra vos modifications permanentes tandis que sess.rollback() les rétablira.

Dans le cas d'un recuit simulé que vous souhaitez valider lorsque vous trouvez une nouvelle meilleure solution. À tout moment ultérieur, vous pouvez invoquer rollback() pour rétablir le statut à cette position.

+0

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

+0

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. –

+0

Cela ne change pas grand-chose mais la fonction 'rollback()' est très utile car je passe de "state" à "state". – PizzAzzra

0

Vous ne voulez pas copier des objets sqlalchemy comme ça. Vous pouvez implémenter vos propres méthodes qui rendent les copies assez facilement, mais ce n'est probablement pas ce que vous voulez. Vous ne voulez pas de copies d'étudiants et de projets dans votre base de données? Donc, ne copiez pas ces données.

Vous avez donc un dictionnaire qui contient vos allocations. Au cours du processus, vous ne devez jamais modifier les objets SQLAlchemy. Toutes les informations pouvant être modifiées doivent être stockées dans ces dictionnaires. Si vous devez modifier les objets pour en tenir compte, recopiez les données à la fin.

+0

@Winston: Je souhaite pouvoir mettre en œuvre mes propres méthodes pour faciliter la copie, mais je suis presque à court de temps et je suis encore un débutant en programmation Python (et en programmation), ce qui me semble un peu intimidant :) Ouais, j'ai un dictionnaire qui contient les allocations - je ne lui ajoute que des lignes et je ne le touche jamais sauf pour interroger l'information.Ce sont les dictionnaires «étudiant», «projet» et «conférenciers» que je change pour le recuit simulé. – PizzAzzra

+0

[suite] Jusqu'à présent, SQLAlchemy ne s'est pas réellement plaint, bien que ce que je fais soit - selon les réponses à mes questions - erroné (changer un objet mappé après qu'il ait été ajouté à la session). Mon problème principal est que je dois utiliser le concept de 'deepcopy' mais je ne peux pas. La raison pour laquelle j'ai mappé 'Student', et al est parce que je devais garder mon mappage d'allocation très simple. Ainsi, les ID d'étudiant dans Allocation sont des clés étrangères dans la table mappée par l'étudiant (pour que je puisse interroger les informations sur les étudiants si nécessaire). C'est un peu fou :) – PizzAzzra

+0

@Winston: Maintenant que vous le mentionnez, je viens de me rappeler que j'ai une fonction appelée 'resetSPL()' qui réinitialise les attributs des étudiants, des projets et des conférenciers ... Je ne devrais pas faire ça devrais-je? – PizzAzzra

Questions connexes