void BiasSolver::simpleOptimize( SolverList *list ) { DEBUG_BLOCK; if( list->m_trackList.count() <= list->m_contextCount ) return; // nothing to optimize. All tracks are in the context // first set some random tracks // this prevents the part bias from first fullfilling the easy conditions for( int i = 0; i < m_n / 2; i++ ) { // choose the mutation position int newPos = (KRandom::random() % (list->m_trackList.count() - list->m_contextCount)) + list->m_contextCount; TrackSet set = matchingTracks( newPos, list->m_trackList ); if( !m_allowDuplicates ) set = withoutDuplicate( newPos, list->m_trackList, set, false ); Meta::TrackPtr newTrack; newTrack = getRandomTrack( set ); if( newTrack ) list->setTrack( newPos, newTrack ); } // now go through the complete list again and try to fullfill all for( int newPos = list->m_contextCount; newPos < list->m_trackList.count(); newPos++ ) { TrackSet set = matchingTracks( newPos, list->m_trackList ); if( !m_allowDuplicates ) set = withoutDuplicate( newPos, list->m_trackList, set, true ); Meta::TrackPtr newTrack; newTrack = getRandomTrack( set ); if( newTrack ) list->setTrack( newPos, newTrack ); } }
QUrl FooPlaylist::getNextTrack(FooPlaybackOrder::FooPlaybackOrder order, FooPlayback::FooPlayback playback) { if (shouldCurrentTrackIndex >= 0) { currentTrackIndex = shouldCurrentTrackIndex; shouldCurrentTrackIndex = -1; } int beforeTrackIndex = currentTrackIndex; QUrl newTrack; if (playlist.empty()) { currentTrackIndex = -1; shouldCurrentTrackIndex = -1; } else if (order == FooPlaybackOrder::Random || playback == FooPlayback::random) { newTrack = getRandomTrack(); } else if (currentTrackIndex < 0) { currentTrackIndex = 0; newTrack = playlist.at(currentTrackIndex).file(); } else { if ((playback == FooPlayback::enque && order == FooPlaybackOrder::Default) || (playback == FooPlayback::next && (order == FooPlaybackOrder::Default || order == FooPlaybackOrder::Repeat_Track))) { ++currentTrackIndex; if (currentTrackIndex >= playlist.size()) { currentTrackIndex = -1; } else { newTrack = playlist.at(currentTrackIndex).file(); } } else if (playback == FooPlayback::prev && (order == FooPlaybackOrder::Default || order == FooPlaybackOrder::Repeat_Track)) { --currentTrackIndex; if (currentTrackIndex < 0) { currentTrackIndex = -1; } else { newTrack = playlist.at(currentTrackIndex).file(); } } else if (order == FooPlaybackOrder::Repeat_Playlist && (playback == FooPlayback::enque || playback == FooPlayback::next)) { ++currentTrackIndex; if (currentTrackIndex >= playlist.size()) { currentTrackIndex = 0; } newTrack = playlist.at(currentTrackIndex).file(); } else if (playback == FooPlayback::prev && order == FooPlaybackOrder::Repeat_Playlist) { --currentTrackIndex; if (currentTrackIndex < 0) { currentTrackIndex = playlist.size() - 1; } newTrack = playlist.at(currentTrackIndex).file(); } else if ((playback == FooPlayback::enque && order == FooPlaybackOrder::Repeat_Track) || playback == FooPlayback::play) { newTrack = playlist.at(currentTrackIndex).file(); } } emit metaChanged(beforeTrackIndex); if (currentTrackIndex >= 0) { emit metaChanged(currentTrackIndex); } return newTrack; }
void BiasSolver::annealingOptimize( SolverList *list, int iterationLimit, bool updateStatus ) { DEBUG_BLOCK; if( list->m_trackList.count() <= list->m_contextCount ) return; // nothing to optimize. All tracks are in the context SolverList originalList = *list; /* * The process used here is called "simulated annealing". The basic idea is * that the playlist is randomly mutated one track at a time. Mutations that * improve the playlist (decrease the energy) are always accepted, mutations * that make the playlist worse (increase the energy) are sometimes * accepted. The decision to accept is made randomly based on a special * probability curve that changes as the algorithm progresses. * * Accepting some bad mutations makes the algorithm resilient to getting * stuck in local minima (playlists that are not optimal but can't be * improved by making just one change). There is much more reading available * on the internet or your local library. */ double T = SA_INITIAL_TEMPERATURE; TrackSet universeSet( m_trackCollection, true ); double oldEnergy = 0.0; int giveUpCount = 0; while( iterationLimit-- && list->energy() >= epsilon() && !m_abortRequested ) { // if the energy hasn't changed in SA_GIVE_UP_LIMIT iterations, we give // up and bail out. if( oldEnergy == list->energy() ) giveUpCount++; else { oldEnergy = list->energy(); giveUpCount = 0; } if( giveUpCount >= SA_GIVE_UP_LIMIT ) break; // choose the mutation position int newPos = (KRandom::random() % (list->m_trackList.count() - list->m_contextCount)) + list->m_contextCount; // choose a matching track or a random one. Prefere matching Meta::TrackPtr newTrack; if( iterationLimit % 4 ) { TrackSet set = matchingTracks( newPos, list->m_trackList ); if( !m_allowDuplicates ) set = withoutDuplicate( newPos, list->m_trackList, set, false ); newTrack = getRandomTrack( set ); } else { if( !m_allowDuplicates ) newTrack = getRandomTrack( withoutDuplicate( newPos, list->m_trackList, universeSet, false ) ); else newTrack = getRandomTrack( universeSet ); } if( !newTrack ) continue; // debug() << "replacing"<<newPos<<list->m_trackList[newPos]->name()<<"with"<<newTrack->name(); SolverList newList = *list; newList.setTrack( newPos, newTrack ); double p = 1.0 / ( 1.0 + exp( (newList.energy() - list->energy()) / list->m_trackList.count() / T ) ); double r = (double)KRandom::random() / (((double)RAND_MAX) + 1.0); // accept the mutation ? if( r <= p ) *list = newList; // cool the temperature T *= SA_COOLING_RATE; if( updateStatus && iterationLimit % 100 == 0 ) { debug() << "SA: E = " << list->energy(); int progress = (int)(100.0 * (1.0 - list->energy())); emit statusUpdate( progress >= 0 ? progress : 0 ); } } // -- use the original list if we made it worse if( list->energy() > originalList.energy() ) *list = originalList; }