[Risolto] [C++] Scelta contenitore e metodo di stampa

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Avatar utente
paper0k
Rampante Reduce
Rampante Reduce
Messaggi: 7220
Iscrizione: lunedì 2 ottobre 2006, 13:39
Contatti:

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da paper0k »

l3on4rdo ha scritto: L'ipotesi di un marcatore, che avevo preso in considerazione, l'ho dovuta scartare semplicemente perché avrebbe comportato, per ogni iterazione della simulazione, (fino ad) alcune decine di migliaia di controlli "inutili"... e il tutto si ripete per un numero di iterazioni dell'ordine di alcune centinaia di miglia (fino ad un massimo di qualche milione...)
Quindi ho optato per l'effettiva rimozione delle pedine che devo ignorare in seguito.
Si può evitare, ad esempio usando un NullObject accoppiato con uno State, ad esempio così:

Codice: Seleziona tutto

class Value {
	public:
		Value() {}
		virtual ~Value() {}
		virtual void set(int new_value) { value=new_value; }
		virtual void print() { std::cout << value << std::endl; }
	private:
		int value;
};
class NullValue : public Value {
	public:
		NullValue() : Value() {}
		virtual ~NullValue() {}
		void set(int new_value) { }
		void print() { }
};
class Entry {
	public:
		Entry() : deactivated(), active(), status(&deactivated) {}
		virtual ~Entry() {}
		void set(int value) { status=&active; status->set(value); }
		void remove() { status = static_cast<Value*>(&deactivated); }
		void print() { status->print(); }
	private:
		Value *status;
		NullValue deactivated;
		Value active;
};
ovviamente da adattare in base alle tue esigenze e, preferibilmente, trasformarlo in un template così da essere usato con qualsiasi tipo ed il cambio di un puntatore è certamente più veloce di una remove :)
l3on4rdo ha scritto: Il fatto è che, o decido di fare una prima iterazione sulla lista per muovere tutte le pedine e poi faccio una successiva iterazione di rimozione con remove_if, oppure decido di fare una singola iterazione sulla lista, come poi ho deciso di fare.
Non ho visto il codice, quindi non so se applicabile al tuo caso, ma con il remove_if prevede un predicato, eventualmente puoi eseguire li delle operazioni prima di restituire l'ok alla cancellazione. Appena ho un po di tempo gli darò un'occhiata ;)
l3on4rdo ha scritto: Purtroppo sono un semplice fisico matematico e non ho studi in programmazione alle spalle, a parte un ridicolo corso di C.
Io nemmeno quello! (rotfl)

Buon divertimento ;)

Ciao
Chiunque può essere ragionevole, ma esser sani di mente è raro (Oscar Wilde)
Wiki|Blog|Twitter|Identi.ca|last.fm
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Sperando che fin qui sia tutto chiaro, datemi cinque minuti e vi posto alcuni esempi di righe di comando per lanciare simulazioni, non troppo avide di risorse, ma che riproducano il problema.

(edit:
mi stanno cacciando dalla biblioteca, quindi aspetto di arrivare a casa per postare le righe di comando "incriminate")
Ho inavvertitamente cancellato la directory in cui mi ero messo alcune simulazioni "problematiche", per poter poi dare i parametri con cui lanciarle ;D

Sto per finire uno script in python (che avevo comunque in mente di fare) che mi "pizzica" tutti i file con ripetizioni.
Appena concluso posto il resto di quanto serve per riprodurre il problema.
Scusatemi, ma ieri tornato a casa ero fuso e per sbaglio ho eliminato la directory...
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

Cose che capitano. Piuttosto, il mio passato da moderatore si fa sentire: visto che ora non si parla più di scelta contenitore e metodo di stampa, che ne diresti di postare quel che devi inaugurando una nuova discussione?
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

bite ha scritto: Cose che capitano. Piuttosto, il mio passato da moderatore si fa sentire: visto che ora non si parla più di scelta contenitore e metodo di stampa, che ne diresti di postare quel che devi inaugurando una nuova discussione?
Caro bite, a prescindere dal fatto che moderatore o meno, tu sei uno di quelli i cui consigli vanno ascoltati!
(e lo penso da quando ho iniziato a leggere i tuoi interventi qui sul forum, molto prima che diventassi moderatore ;) )

Detto questo, ti ho mandato un messaggio privato in merito all'apertura di una nuova discussione.
Non so se aprirne una nuova o se sia il caso semplicemente di cambiare il titolo, per esempio aggiungendo "problema" :)
Ultima modifica di l3on4rdo il giovedì 23 febbraio 2012, 18:28, modificato 1 volta in totale.
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

Solo per essere sicuro di non aver perso nulla: i set di parametri sono poi riemersi dai meandri del tuo disco?  ;D
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

bite ha scritto: Solo per essere sicuro di non aver perso nulla: i set di parametri sono poi riemersi dai meandri del tuo disco?  ;D
Allora, ho "sospeso" la discussione perché in seguito al tuo consiglio ho contattato un moderatore per capire come procedere.
Posso modificare il titolo e continuare qui (onde evitare di spostare i messaggi o fare altre cose per non rendere incomprensibili due discussioni invece che una ;D).
Poi questo fine settimana ho dovuto lavorare di notte, ma ho recuperato i parametri che riproducono il problema.
Domani li posto, facendo anche il punto della situazione.
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

In attesa di poter giocare con i parametri, avendo mezz'ora a disposizione ho dato un'occhiata a volo d'uccello  ;D al codice che hai postato e mi sono sorte spontanee le seguenti considerazioni (ovviamente non esaustive):
  • Non sono stato a controllare i dettagli, ma nel main leggi da uno stringstream con operator>> un int non inizializzato. Tieni presente che se stringstream è vuoto l'intero non viene modificato, quindi sarebbe meglio inizializzarlo a un default.
  • Per i signed long andrebbe usata la funzione labs e non abs. Può fare differenza se stai usando un sistema a 64 bit.
  • Vedo che usi l'iteratore ritornato da std::list::erase, me ne avevi parlato. Visto che gli iteratori di list sono bidirezionali, resta il dubbio se l'iteratore ritornato punti all'elemento precedente a quello eliminato, oppure al successivo. Hai fatto un programmino di test per verificarlo?
  • std::map e altri oggetti che possono essere corposi (comprese le std::string) non andrebbero mai passati per copia come argomenti, ma per reference se devono poter essere modificati o per const reference se non devono poter essere modificati. Vedo che in alcuni casi lo fai e in altri invece passi per copia.
  • Similmente non conviene che std:map e simili vengano ritornati da una funzione; una possibile soluzione è che il chiamante li definisca, vuoti, e li passi come argomento reference (ovviamente non const) alla funzione che li deve riempire.
  • Userei bool invece di int per il risultato di una comparazione e altri valori logici, anche se è più che altro questione di stile.
Ciao
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Ciao bite, allora:
Non sono stato a controllare i dettagli, ma nel main leggi da uno stringstream con operator>> un int non inizializzato. Tieni presente che se stringstream è vuoto l'intero non viene modificato, quindi sarebbe meglio inizializzarlo a un default.
Sì, hai pienamente ragione.
Dovrei sistemare l'acquisizione dati, magari con una try o un controllo sui flag di stringstream (come mi ha suggerito in un'altra discussione crap0101, per esempio), o magari con un parser migliore (di quello che mi sono scritto) e già implementato, di quelli che si trovano magari online, ma non sono riuscito a capire quale usare.
Questo che mi fai notare, però, non è da ricollegare con il problema, in quanto io ho la stampa (in un file di report) di tutti i parametri passati alla simulazione, del conteggio di alcuni eventi interni e del seme di inizializzazione.
I parametri sono sempre "ben inizializzati" al valore.
Inoltre, il controllo che il parametro ci sia (e che quindi stringstream non sia vuoto) avviene tramite l'orrendo (ma funzionale) parser che mi sono scritto per la simulazione. Tale parser, se non vengono passati per bene tutti i parametri, si lagna, stampa le lamentele a video e non fa partire la simulazione.
Per i signed long andrebbe usata la funzione labs e non abs. Può fare differenza se stai usando un sistema a 64 bit.
Sì sono praticamente solo sistemi a 64 bit, quelli su cui girano le simulazioni.
Controllo appena ho un attimo.
Vedo che usi l'iteratore ritornato da std::list::erase, me ne avevi parlato. Visto che gli iteratori di list sono bidirezionali, resta il dubbio se l'iteratore ritornato punti all'elemento precedente a quello eliminato, oppure al successivo. Hai fatto un programmino di test per verificarlo?
Ti pare possibile che io non abbia fatto almeno un test per verificarlo? ;D
Scherzi a parte, ho controllato (e ricontrollato ;) ) e erase ritorna questo:
Return value
A bidirectional iterator pointing to the new location of the element that followed the last element erased by the function call, which is the list end if the operation erased the last element in the sequence.
In merito a questa rimozione, come dici, avevamo parlato.
Ho usato inizialmente le precauzioni di cui parlammo (ovvero di usare un iteratore d'appoggio per eliminare l'elemento per non far incasinare l'iterazione), ma poi ho notato appunto che il metodo erase funziona "a meraviglia" (nel senso che ogni test (e ne ho fatti parecchi) è andato bene).
std::map e altri oggetti che possono essere corposi (comprese le std::string) non andrebbero mai passati per copia come argomenti, ma per reference se devono poter essere modificati o per const reference se non devono poter essere modificati. Vedo che in alcuni casi lo fai e in altri invece passi per copia
Esatto :)
E questo è proprio quello che avrei poi chiesto, appena postati i parametri, in merito alla stampa dei contenitori.
Nel senso che io li passo per referenza se li devo modificare, ma per esempio per copia quando li devo stampare.
Leggendo [url=http://C++%20Primer]C++ Primer[/url], ho appunto appreso che conviene passarli per referenza (const) in questo secondo caso e in quelli analoghi.
È proprio uno dei due motivi per cui ho aperto la discussione.
Rimandiamo per un momento la questione (tanto io me lo sono segnato, che devo fare la domanda), che pongo appena riesco a scrivere questo benedetto messaggio con i dati iniziali.
Similmente non conviene che std:map e simili vengano ritornati da una funzione; una possibile soluzione è che il chiamante li definisca, vuoti, e li passi come argomento reference (ovviamente non const) alla funzione che li deve riempire.
Non ti ho capito qui  :-[
Userei bool invece di int per il risultato di una comparazione e altri valori logici, anche se è più che altro questione di stile
Hai ancora pienamente ragione.
Purtroppo questa simulazione è la sintesi di alcune parti iniziate a scrivere quando sapevo pochissimo di C/C++ e di alcune parti che ho scritto quando ne sapevo un po' di più.
Alcune cose non le ho modificate e questa dei bool è una.

Vi aggiorno quanto prima.
Scusatemi davvero, ma non dipende solo da me.
Per esempio... mi hanno spianato l'account su cui faccio le simulazioni, per sbaglio, e ho dovuto ricominciare a fare alcuni lavori.
Pazientate che ho quasi fatto  :-*
Ultima modifica di l3on4rdo il lunedì 5 marzo 2012, 19:28, modificato 1 volta in totale.
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

l3on4rdo ha scritto:
Similmente non conviene che std:map e simili vengano ritornati da una funzione; una possibile soluzione è che il chiamante li definisca, vuoti, e li passi come argomento reference (ovviamente non const) alla funzione che li deve riempire.
Non ti ho capito qui  :-[
Intendo: non

Codice: Seleziona tutto

std::map<pippo, pluto> creamiLaMappa ();
...
map<pippo,pluto> mappa = creamiLaMappa ();
ma:

Codice: Seleziona tutto

void riempimiLaMappa (std::map<pippo,pluto> & mappa);
...
map<pippo,pluto> mappa;
riempimiLaMappa (mappa);
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Signori, il problema è risolto!  (good)
Dopo un bel po' di delirio, ho cambiato il generatore dei numeri pseudocasuali.
Nel programma, inizialmente, usavo la famiglia di funzioni rand48 della stdlib (man drand48).
L'ho sostituita con il generatore (molto migliore) Mersenne Twister.
Oltre a non avere più malfunzionamenti, le simulazioni sono molto più veloci e l'estrazione (di un numero mooolto elevato di numeri) impiega circa un quarto del tempo che serve alle funzioni della famiglia rand48.

Fatto questo preambolo, visto che mi è comunque rimasto di capire come mai succedeva quel delirio, riprenderei con le cose rimaste da dire circa le simulazioni problematiche. E se qualcuno mi vuole venire appresso in questo delirio, perché magari sa capire meglio di me da cosa possa dipendere e vuole illuminarmi, è il benvenuto :)
Poi, nei prossimi messaggi, concluderei con la seconda questione, quella relativa alle mie perplessità circa il migliore metodo per la stampa degli elementi di un contenitore, da cui pensavo potesse dipendere questo problema.

Il codice delle simulazioni (ovviamente quello che dà problemi) lo riallego in questo messaggio.
(è lo stesso del precedente allegato, ma ho tolto i file .o compilati che per distrazione avevo messo in quello)
Purtroppo non ho un file make, ma una volta scaricato il "pacchetto", lo si compila con:

Codice: Seleziona tutto

g++ -c *.cpp

e poi, per creare l'eseguibile:

Codice: Seleziona tutto

g++ -o MetastableRandWalk *.o
La spiegazione del problema è in questo messaggio.

Per riprodurre il problema, si può procedere in due modi.
O si genera un set generico di simulazioni e poi con l'aiuto di uno script in python (script e spiegazione che accludo più sotto) si cerca tra i file di output quelli con le ripetizioni.
Oppure si lanciano le simulazioni singole con i parametri specifici che dovrebbero riprodurre il problema.
(dico "dovrebbero" perché io ho fatto delle prove e con gli stessi parametri le simulazioni si bloccano su computer differenti, ma mentre so che il mio processore è a 64 bit, non so se lo siano anche quelli degli altri computer o se siano a 32, anche se è più probabile che siano a 64 bit)

Le singole simulazioni problematiche si possono, quindi, lanciare con il comando:

Codice: Seleziona tutto

./MetastableRandWalk -set 100 -file NUMERO_FILE -part 316 -bound 100 -step 10 -visc 1.0 -length 200000 -print 200 -seed SEME
stando attenti a dare NUMERO_FILE sempre diverso (altrimenti, come spiegato sotto, i file di output coincidono e l'output di una simulazione viene appeso a quella precedente) e sostituendo al posto di SEME, per esempio, uno dei valori di questa lista di semi (che sono alcuni di quelli che ho constatato che danno problemi):

Codice: Seleziona tutto

Seme: 1331645036
Seme: 1331645075
Seme: 1331645098
Seme: 1331645122
Seme: 1331645145
Seme: 1331646552
Seme: 1331646592
Seme: 1331646615
Seme: 1331646638
Seme: 1331646662
Seme: 1331646733
Seme: 1331646756
Seme: 1331646780
Seme: 1331646883
Seme: 1331646906
Seme: 1331647618
Seme: 1331647641
Seme: 1331647665
Seme: 1331647688
Seme: 1331647711
Seme: 1331647765
Seme: 1331647788
Seme: 1331647811
Seme: 1331647834
Seme: 1331647889
Seme: 1331647912
Seme: 1331647936
Seme: 1331647959
Seme: 1331647982
Seme: 1331648005
Seme: 1331648027
Visto che i semi sono diversi, si potrebbe mettere tale valore come NUMERO_FILE.
Io ho inserito tale parametro, come si capisce più giù, per numerare le simulazioni di un set.

Alternativamente, potete generare un set di TOT simulazioni (che richiedono poche risorse e durano una manciata di minuti) con il comando:

Codice: Seleziona tutto

for i in `seq 1 1 TOT`; do ./MetastableRandWalk -set 100 -file $i -part 316 -bound 100 -step 10 -visc 1.0 -length 200000 -print 200; done
(ovviamente al posto di TOT ci va un numero :) )

In entrambi i casi, nella stessa cartella in cui si lancia il programma, viene creata una cartella di nome:

Codice: Seleziona tutto

set100-100-10-1.0-316-200000-200
in cui ci saranno delle sottocartelle:

Codice: Seleziona tutto

simulazioneNUMERO_FILE
(se non si varia NUMERO_FILE, come avvertito sopra, il problema è che si mette la simulazione dentro la stessa directory già presente).

All'interno di queste cartelle simulazione ci sono i tre file:

Codice: Seleziona tutto

crown_dens.txt
frame_dens.txt
events_count.txt
I file in cui si trovano le ripetizioni sono i primi due (se ci sono in crown_dens.txt ci saranno anche in frame_dens.txt "ovviamente").

Se i file sono stati creati con la prima procedura dovrebbero mostrare il problema delle ripetizioni nelle stampe ("dovrebbero", sempre perché non ho la certezza assoluta che anche su un processore a 32 bit diano problemi).
Se invece è stato generato un set di simulazioni con il secondo comando (il ciclo for), o anche se si vuole controllare che una delle simulazioni lanciate con uno dei semi elencati sia problematica anche sulle vostre macchine, si possono isolare le simulazioni problematiche (e anche quelle "tranquille") usando questo script in python, che vi illustro nel dettaglio.

Codice: Seleziona tutto

#! /usr/bin/env python
# -* coding: iso-8859-15 -*-

from sys import argv
import argparse, os

def singleDuplicateLinesSetCheck(inFilePath, untilMatch):
	readPartialLines = []
	partialRepetitions = []
	singleSetFlag = False
	if not untilMatch:
		untilMatch = "'tempo'"
	if not untilMatch.startswith("'"):
		untilMatch = "'".join(['',untilMatch,''])
	with open(inFilePath) as inFile:
		for lineNumber, line in enumerate(inFile):
			partialLine = line[:line.find(untilMatch) - 2]
			if partialLine in readPartialLines:
				if len(partialRepetitions) >= 2:
					firstRep = partialRepetitions[0][1]
					firstRep = firstRep[:firstRep.find(untilMatch) - 2]
					if partialLine == firstRep:
						singleSetFlag = True
						break
				partialRepetitions.append((lineNumber + 1, line))
			else:
				readPartialLines.append(partialLine)
	if partialRepetitions:
		firstRep = partialRepetitions[0][1]
		firstRep = firstRep[:firstRep.find(untilMatch) - 2]
		firstRowNumber = readPartialLines.index(firstRep) + 1
	else:
		firstRowNumber = 0
	return firstRowNumber, singleSetFlag, partialRepetitions

parser = argparse.ArgumentParser()
parser.add_argument('-f', '--file', nargs='+', action="store", required=True)
parser.add_argument('--word', action="store")
parser.add_argument('--showSet', action="store_true")
parser.add_argument('--maxPrint', action="store", type=int)

arguments = parser.parse_args()
print arguments

word = arguments.word
for filePath in arguments.file:
	try:
		firstRowNum, singleSetFlag, repetita = singleDuplicateLinesSetCheck(filePath, word)
		print "*"*8
		eventsPath = os.path.dirname(filePath) + os.sep + 'events_count.txt'
		events = open(eventsPath).readlines()
		for event in events:
			if event.startswith('Seme'):
				seme = '-' + event.rstrip()
				break
		if repetita:
			print "-Ripetizioni in: %s" % filePath
			print seme
			print "-Prima ripetizione della riga {0} alla riga numero: {1}\n{2}".format(firstRowNum, repetita[0][0], repetita[0][1].rstrip())
			if not singleSetFlag:
				print "-Numero di righe ripetute indeterminabile; potrebbe essere %i" % (repetita[0][0] - firstRowNum)
			else:
				rows = len(repetita)
				print "-Numero di righe ripetute: %d" % rows
				if arguments.showSet:
					if arguments.maxPrint != None:
						if rows > arguments.maxPrint:
							print "-Numero di righe ripetute oltre il limite di %i previsto per la stampa" % arguments.maxPrint
							continue
						print "-Righe ripetute:"
						for index, content in repetita:
							print content.rstrip()
					else:
						print "-Righe ripetute:"
						for index, content in repetita:
							print content.rstrip()
		else:
			print "-Nessuna ripetizione in: %s" % filePath
			print seme
	except IOError as (errno, strerror):
		print "{0}\nI/O error({1}): {2}".format(filePath, errno, strerror)
Perché questo script possa funzionare, si deve scaricare il codice del modulo argparse da questo link (andate tranquilli, perché è un link ufficiale, fornito dal sito del progetto python.org) e metterlo nella stessa directory dello script in python per l'analisi delle simulazioni (oltre ad avere ovviamente l'interprete python installato).

Quindi, sia che si sia prodotto un insieme di simulazioni, sia che si siano lanciate direttamente quelle che a me danno problemi, per produrre un elenco con l'analisi delle simulazioni interne al set o per verificare se la simulazione singola si è bloccata o no, basta lanciare lo script python (ovviamente avendogli dato i permessi di esecuzione) con il comando:

Codice: Seleziona tutto

path_per_lo_script_python -f lista_path_file_da_controllare
Questo script, quindi, con il suo output (che illustro a breve) o dà la conferma che una delle specifiche simulazione lanciate con un seme si è bloccata anche a voi, oppure serve per fare un elenco di simulazioni bloccate tra quelle di un data set.
Il primo caso è abbastanza banale, si tratta di dare il comando qui sopra con il path del file da controllare (io di norma controllo solo i file crown_dens.txt) e vedere l'output singolo.
Nel caso si volesse analizzare un set di simulazioni, l'ideale, per non stare a guardare tutto l'output e per non scrivere ogni singolo path dei file da controllare, sarebbe (concentrandosi solo sui crown_dens.txt, perché tanto, come detto, se le ripetizioni sono in crown_dens sono anche in frame_dens) mettersi nella directory che contiene la cartella del set di simulazioni e dare il comando:

Codice: Seleziona tutto

path_per_lo_script_python -f `find set100-100-10-1.0-316-200000-200 -name "crown*" sort -V` | grep -i ripetizioni
Per fare un esempio, mentre scrivevo questo messaggio ho lanciato un set da 50 simulazioni (con il comando sopra) ottenendo:

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalkDifettoso$ ls set100-100-10-1.0-316-200000-200/
totale 200K
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 14:20 simulazione01
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 14:23 simulazione02
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 14:23 simulazione03
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 14:24 simulazione04
...
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 15:12 simulazione47
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 15:13 simulazione48
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 15:13 simulazione49
drwxr-xr-x 2 l3on4rdo l3on4rdo 4,0K 2012-03-13 15:13 simulazione50
Di queste, produco un elenco con le informazioni utili (e mi metto l'output nel file ~/Scrivania/problematiche.txt) dando (da dentro la directory che contiene la directory set100-100-10-1.0-316-200000-200) il comando:

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalkDifettoso$ ~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set100-100-10-1.0-316-200000-200/ -name "crown*"` > ~/Scrivania/problematiche.txt
In questo modo, nel file, ho la descrizione esatta della situazione, con informazioni del tipo:

Codice: Seleziona tutto

********
-Nessuna ripetizione in: set100-100-10-1.0-316-200000-200/simulazione42/crown_dens.txt
-Seme: 1331647857
oppure:

Codice: Seleziona tutto

********
-Ripetizioni in: set100-100-10-1.0-316-200000-200/simulazione39/crown_dens.txt
-Prima ripetizione della riga 101 alla riga numero: 357
{1: 1.58228e-05, 2: 0.00401064, 3: 0.00750639, 4: 0.00869782, 5: 0.00649113, 6: 0.00645616, 7: 0.00504407, 8: 0.00483857, 9: 0.00756991, 10: 0.00842766, 11: 0.00506011, 'tempo': 71200, 'trapped': 61}
-Numero di righe ripetute: 256
-Seme: 1331647788
a seconda che la simulazione si blocchi o meno.

Da queste informazioni, posso estrarre le informazioni "veramente importanti" circa
il path dei soli file che presentano problemi con il comando:

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalkDifettoso$ ~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set100-100-10-1.0-316-200000-200/ -name "crown*" | sort -V` | grep Ripetizioni | cut -f 2 -d ':'
il cui ouput è:

Codice: Seleziona tutto

 set100-100-10-1.0-316-200000-200/simulazione03/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione05/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione06/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione07/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione08/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione11/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione13/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione14/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione15/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione16/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione20/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione21/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione22/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione28/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione29/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione31/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione32/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione33/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione34/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione35/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione38/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione39/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione40/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione41/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione44/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione45/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione46/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione47/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione48/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione49/crown_dens.txt
 set100-100-10-1.0-316-200000-200/simulazione50/crown_dens.txt

Codice: Seleziona tutto

for filePath in $(~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set100-100-10-1.0-316-200000-200/ -name "crown*" | sort -V` | grep Ripetizioni | cut -f 2 -d ':'); do echo $filePath; done
oppure circa i semi delle simulazioni bloccate:

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalkDifettoso$ for filePath in $(~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set100-100-10-1.0-316-200000-200/ -name "crown*" | sort -V` | grep Ripetizioni | cut -f 2 -d ':'); do grep -i seme `dirname $filePath`/events_count.txt; done
con cui ho ottenuto proprio la lista di semi mostrata sopra.

A margine, faccio presente che lo script implementa anche altre due opzioni:
1°)

Codice: Seleziona tutto

--showSet
con cui si richiede la stampa di un singolo gruppo di righe che si ripetono, qualora questo sia determinabile.
O la stampa dell'avvertimento che il numero di righe non è determinabile
(senza che mi dilungo sui dettagli, può capitare che non si riesca a determinare con certezza il numero di righe ripetute, ma se ne può dare una stima... in pratica capita quando le ripetizioni iniziano "tardi" e non fanno in tempo a essere ristampate tutte le righe, prima che la simulazione termini)

2°)

Codice: Seleziona tutto

--maxPrint MAX
da dare se viene data la precedente opzione.
Serve per limitare la stampa solo al caso in cui il numero di righe del set che si ripete sia minore o uguale al numero MAX passato come parametro

(esiste anche un ulteriore parametro --word, ma per il momento non serve usarlo).

Con questo, penso di aver detto tutto quello che serve.
Sperando di essere stato chiaro, concludo con una caratteristica "molto strana" che ho notato analizzando il problema.
Il numero di righe ripetute è sempre una potenza di 2 e la cosa mi "affascina" parecchio.
Inoltre il problema non dipende da altro che dal particolare seme di inizializzazione.
Quindi se volete fare altre prove, potete cambiare i parametri (considerando che le simulazioni possono diventare molto lunghe) e isolare, per ogni caso, i semi che danno problemi anche in altre condizioni, seguendo le istruzioni qui sopra.

Se qualcuno vuole venire appresso a questo delirio e dare un'occhiata al problema, lo ringrazio anticipatamente.
La cosa è alquanto assurda, perché la soluzione del problema è consistita nella semplice sostituizione dei generatori di numeri casuali con il generatore Mersenne Twister, di cui ho preso il codice (fornito con licenza GPL) in questo sito.
Dopo tale sostituzione ho fatto circa 2000 simulazioni e non ci sono stati malfunzionamenti.
Dico che è una soluzione assurda, perché i generatori che usavo inizialmente sono usati solamente nei codici del file libEvolSmart.cpp (e dal main in cui si richiamano le funzioni in esso implementate).
Per esempio, nella funzione:

Codice: Seleziona tutto

list< pair <signed long, signed long> >
stateInit(unsigned long totalPart, unsigned long bound) {
	list< pair <signed long, signed long> > sysState;
	pair <signed long, signed long> coord;
	for(unsigned long particle=1; particle<=totalPart; particle++) {
		coord.first = (lrand48() % (2*bound + 1)) - bound;
		coord.second = (lrand48() % (2*bound + 1)) - bound;
		sysState.push_back(coord);
	}
	return sysState;
}
è diventata:

Codice: Seleziona tutto

list< pair <signed long, signed long> >
stateInit(unsigned long totalPart, unsigned long bound,
		  CRandomMersenne &RandGen) {
	list< pair <signed long, signed long> > sysState;
	pair <signed long, signed long> coord;
	for(unsigned long particle=1; particle<=totalPart; particle++) {
		coord.first = RandGen.IRandomX(0, 2*bound) - bound;
		coord.second = RandGen.IRandomX(0, 2*bound) - bound;
		sysState.push_back(coord);
	}
	return sysState;
}
e tutte le altre modifiche sono dello stesso tipo.
Quindi non capisco proprio come un ipotetico bug nei generatori della famiglia rand48 (o una correlazione tra i numeri generati) possa aver causato un malfunzionamento di questo tipo.
Perché questi, ora, sono i due unici possibili scenari che vedo come causa di tali malfunzionamenti.

Aspetto un vostro feedback in merito al problema (o un eventuale "a Leona'... ma che te si mpazzito!?" ;D ) per parlare della mia perplessità in merito ai metodi di stampa (e poi mettere [Risolto] a questa discussione ;) )

Ciao e grazie davvero per l'attenzione.
Allegati
MetastableRandWalk.tar.bz2
(13.8 KiB) Scaricato 22 volte
Ultima modifica di l3on4rdo il mercoledì 14 marzo 2012, 3:08, modificato 1 volta in totale.
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

Non credo sia facile capire nei dettagli perché un generatore di numeri random dava quei problemi. O per lo meno, bisognerebbe forse avere degli strumenti matematici al di fuori della mia portata (teoria ergodica).

Mi accontenterei di verificare se nell'output della drand48, per i semi che hai indicato, ci sono dei cicli: se ci sono, è plausibile che anche la simulazione abbia un comportamento ciclico.

Visto che queste funzioni non hanno uno stato interno (a parte il precedente numero random calcolato), se si trova  che in una finestra di, poniamo, 10000 chiamate successive un output si ripete, significa che c'è un ciclo di dimensione inferiore a 10000. Scriverò qualche riga per verificarlo (la cosa interessa anche me, come ti ho detto).
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

l3on4rdo ha scritto: le simulazioni sono molto più veloci e l'estrazione (di un numero mooolto elevato di numeri) impiega circa un quarto del tempo che serve alle funzioni della famiglia rand48.
Questo mi stupisce. Per quanto possa essere efficiente il Mersenne twister, non capisco come possa essere più veloce di un generatore lineare congruenziale, che fa solo un prodotto, una somma e un modulo. Sicuro di non aver cambiato nient'altro?
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Per il momento aggiungo solo che per via di questo malfunzionamento ho davvero sofferto molto.
Inizialmente credevo fosse per via del fatto che non avevo capito una cippa di C/C++
Poi mi sono un po' tranquillizzato e ho deciso di affrontare il problema in modo "massiccio".
Ma fino a che non l'ho risolto (e con una soluzione che non aveva nulla a che fare con le librerie e le parti del programma da me scritte!), sono stato davvero in ansia.
Anche perché mi ha rallentato in maniera davvero enorme la tesi!!!  :(

Altre considerazioni (compreso il nuovo codice, se lo vuoi guardare) le aggiungo a breve, quando sono un po' più sereno.
Comunque sì, ho solamente cambiato le chiamate ai generatori  :-\
Avatar utente
bite
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 3798
Iscrizione: sabato 19 maggio 2007, 22:10

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da bite »

bite ha scritto: Visto che queste funzioni non hanno uno stato interno (a parte il precedente numero random calcolato), se si trova  che in una finestra di, poniamo, 10000 chiamate successive un output si ripete, significa che c'è un ciclo di dimensione inferiore a 10000. Scriverò qualche riga per verificarlo (la cosa interessa anche me, come ti ho detto).
Non tenevo conto che lrand48 si basa su un registro interno a 48 bit di cui da via i 32 più significativi, quindi il fatto che un output venga ripetuto identico non significa necessariamente che si sia entrati in un ciclo.

Infatti con uno dei tuoi semi problematici, 1331645036, chiamando dieci milioni di volte la lrand48 si hanno 47 'doppioni' (entro una finestra di 10000) ma nessun ciclo.

Puoi sapere quante volte viene chiamato il generatore random prima che la simulazione si incasini?
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Allora, riguardo la velocità di esecuzione, sto facendo alcuni test sull'estrazione di 500000 numeri e sto vedendo che (di pochissimo) il mersenne è più veloce della famiglia rand48
Non sono però test affidabili, perché per esempio li sto facendo con il comando time di bash, quindi appena ho modo verifico in maniera più "seria".
Puoi sapere quante volte viene chiamato il generatore random prima che la simulazione si incasini?
Posso fare una stima calcolando il fatto che per ogni pedina libera ho un tiro a sorte ad ogni turno (per determinare dove si sposta); da quando la trappola è popolata, inoltre, ho un ulteriore tiro (per controllare se la trappola emette).
Al momento mi è venuto il dubbio che il problema potesse presentarsi o meno a seconda dell'intervallo di stampa... ma "fortunatamente" non dipende da esso, a meno ovviamente di presentarsi prima o dopo
Ovvero se con 200 come intervallo di stampa il problema si presenta dopo 10 stampe

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalk/materialeIkitt/ModuliPerMessaggioIkitt/randwalkSmartIkitt$ ~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set1001-100-10-1.0-316-200000-200/ -name "crown*" | sort -V`
********
-Ripetizioni in: set1001-100-10-1.0-316-200000-200/simulazione01/crown_dens.txt
-Seme: 1331645036
-Prima ripetizione della riga 159 alla riga numero: 415
{1: 0.000490506, 2: 0.00895213, 3: 0.00381714, 4: 0.00717559, 5: 0.00632801, 6: 0.0064547, 7: 0.00816846, 8: 0.00698811, 9: 0.00487789, 10: 0.0058431, 11: 0.00637244, 'tempo': 82800, 'trapped': 61}
-Numero di righe ripetute: 256
con 100 si presenterà dopo 20 stampe (precisamente dopo il doppio meno due stampe):

Codice: Seleziona tutto

l3on4rdo@l3on4rdo-laptop:~/Documenti/tesi/tesi-03-05-2012/codiciSimulazioni/randomWalk/materialeIkitt/ModuliPerMessaggioIkitt/randwalkSmartIkitt$ ~/Documenti/tesi/tesi-03-05-2012/scriptAnalisiDati/repetitionSingleSetCheck.py -f `find set1001-100-10-1.0-316-200000-100/ -name "crown*" | sort -V`
*******
-Ripetizioni in: set1001-100-10-1.0-316-200000-100/simulazione01/crown_dens.txt
-Seme: 1331645036
-Prima ripetizione della riga 316 alla riga numero: 828
{1: 0.000791139, 2: 0.00846809, 3: 0.00388107, 4: 0.00762704, 5: 0.00610993, 6: 0.00624855, 7: 0.00808031, 8: 0.00708794, 9: 0.00512491, 10: 0.00581715, 11: 0.00629564, 'tempo': 82700, 'trapped': 61}
-Numero di righe ripetute: 512
Sapendo che (per ogni turno) ogni pedina libera comporta un tiro random, e che la trappola popolata ne comporta un altro, posso sapere esattamente quante particelle sono intrappolate ad ogni turno facendo la stampa ad ogni iterazione (printTime 1)
In questo modo posso dirti esattamente quanti numeri sono stati estratti al momento dello "scapocciamento"
E dal momento che la simulazione si "inceppa" posso dirti anche ogni ciclo quante estrazioni comporta (una per ogni particella non intrappolata e una per la trappola)

Nel finesettimana faccio il conto esatto, perché penso che per non morire è meglio che lo faccio fare a python ;)

ciao
l3on4rdo

Re: [C++] Scelta contenitore e metodo di stampa

Messaggio da l3on4rdo »

Visto che il problema sembra effettivamente risolto (su più di diecimila test), vi ringrazio per il supporto e metto risolto.

Ciao :)
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 9 ospiti