How can I configure the OptaPlanner to restart only certain solver phases after ProblemFactChange? - optaplanner

In my OptaPlanner-based app I would like to use a ProblemFactChange, and according to https://docs.optaplanner.org/7.13.0.Final/optaplanner-docs/html_single/index.html#problemFactChange it will restart all solver phases.
The problem is a partitioned search phase that I don't want to restart - the solver should proceed to the next phase (CH) and then to a local search phase.
Is it possible to make it work somehow?

Interesting use case. My first thought would be to use a custom Termination, but that would still cause the phase startup overhead - and the Termination interface isn't public api.
This is an RFE really - we need to be able to plug in a conditional in the phase config.

I think I managed to get through that. I created two solver behaviors: the first one removes a partitioned search phase from the solver when it is finished, and the second one restores phase start times in order to preserve termination end.
A base class for behaviors, please note that I use a non public API.
I have placed all classes in the original OptaPlanner's package in order to have access to protected, and package protected classes without the help of reflection.
package org.optaplanner.core.impl.phase.scope
import org.optaplanner.core.impl.solver.DefaultSolver
/**
* Base interface for all behaviors
*/
abstract class SolverBehavior<T>(protected val solver: DefaultSolver<T>) {
abstract fun apply()
abstract fun unapply()
}
The following behavior removes a partitioned search phase from solver phases after it is completed
package org.optaplanner.core.impl.solver
import org.optaplanner.core.impl.partitionedsearch.PartitionedSearchPhase
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener
import org.optaplanner.core.impl.phase.scope.AbstractPhaseScope
import org.optaplanner.core.impl.phase.scope.AbstractStepScope
import org.optaplanner.core.impl.phase.scope.SolverBehavior
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope
/**
* Ensures that [org.optaplanner.core.impl.partitionedsearch.PartitionedSearchPhase] run only once
*/
class RunPartitionedSearchOnceBehavior<T>(solver: DefaultSolver<T>) : SolverBehavior<T>(solver) {
private var solverStartCount: Int = 0
private val isFirstSolverCycle: Boolean get() = solverStartCount <= 1
override fun apply() {
registerForSolverEvents()
}
override fun unapply() {
unregisterFromSolverEvents()
}
private fun registerForSolverEvents() {
solver.addPhaseLifecycleListener(phaseLifecycleListener)
}
private fun unregisterFromSolverEvents() {
solver.removePhaseLifecycleListener(phaseLifecycleListener)
}
private fun handleSolverStart() {
incrementSolverStartCount()
removePartitionedSearchPhasesIfNecessary()
}
private fun removePartitionedSearchPhasesIfNecessary() {
if (!isFirstSolverCycle) {
removePartitionedSearchPhases()
}
}
private fun removePartitionedSearchPhases() {
val phaseListIterator = solver.phaseList.iterator()
for (phase in phaseListIterator) {
if (phase is PartitionedSearchPhase) {
phaseListIterator.remove()
}
}
}
private fun incrementSolverStartCount() {
solverStartCount++
}
private val phaseLifecycleListener = object : PhaseLifecycleListener<T> {
override fun solvingStarted(solverScope: DefaultSolverScope<T>) = handleSolverStart()
override fun phaseStarted(phaseScope: AbstractPhaseScope<T>) {}
override fun stepStarted(stepScope: AbstractStepScope<T>) {}
override fun solvingEnded(solverScope: DefaultSolverScope<T>) {}
override fun phaseEnded(phaseScope: AbstractPhaseScope<T>) {}
override fun stepEnded(stepScope: AbstractStepScope<T>) {}
}
}
The following behavior (enclosed in a Kotlin file) restores start times for all phases in order to preserve their termination time.
package org.optaplanner.core.impl.phase.scope
import org.optaplanner.core.impl.constructionheuristic.scope.ConstructionHeuristicPhaseScope
import org.optaplanner.core.impl.localsearch.scope.LocalSearchPhaseScope
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener
import org.optaplanner.core.impl.solver.DefaultSolver
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope
import java.lang.IllegalStateException
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
private var AbstractPhaseScope<*>.startDate
get() = Date(startingSystemTimeMillis)
set(value) {
startingSystemTimeMillis = value.time
}
private data class PhaseScopeId constructor(private val phaseClass: Class<*>, private val phaseIndex: Int)
/**
* When solver restarts it restores phase's start time information in order to preserve termination times.
* This helps smooth [org.optaplanner.core.impl.solver.ProblemFactChange] execution
*/
class PreservePhaseStartTimeBehavior<T>(solver: DefaultSolver<T>) : SolverBehavior<T>(solver) {
private var solverStartCount: Int = 0
private var startedPhasesCounters: MutableMap<Class<out AbstractPhaseScope<T>>, AtomicInteger> = mutableMapOf()
private val isFirstSolverCycle: Boolean get() = solverStartCount <= 1
private val phasesStartDates: MutableMap<PhaseScopeId, Date> = mutableMapOf()
private val startTimePreservationPhaseIds: MutableSet<PhaseScopeId> = mutableSetOf()
fun addLocalSearchPhaseStartTimePreservation(solverPhaseIndex: Int) {
startTimePreservationPhaseIds.add(PhaseScopeId(LocalSearchPhaseScope::class.java, solverPhaseIndex))
}
fun addConstructionHeuristicPhaseStartTimePreservation(solverPhaseIndex: Int) {
startTimePreservationPhaseIds.add(PhaseScopeId(ConstructionHeuristicPhaseScope::class.java, solverPhaseIndex))
}
override fun apply() {
registerForSolverEvents()
}
override fun unapply() {
unregisterFromSolverEvents()
}
private fun registerForSolverEvents() {
solver.addPhaseLifecycleListener(phaseLifecycleListener)
}
private fun unregisterFromSolverEvents() {
solver.removePhaseLifecycleListener(phaseLifecycleListener)
}
private fun handleSolverStart() {
incrementSolverStartCount()
resetPhaseStartCounters()
}
private fun handlePhaseStarted(phaseScope: AbstractPhaseScope<T>) {
incrementPhaseStartCount(phaseScope)
savePhaseStartTimeIfNecessary(phaseScope)
restorePhaseStartTimeIfNecessary(phaseScope)
}
private fun savePhaseStartTimeIfNecessary(phaseScope: AbstractPhaseScope<T>) {
if (isFirstSolverCycle) {
phasesStartDates[getPhaseIdForStartedPhase(phaseScope)] = phaseScope.startDate
}
}
private fun restorePhaseStartTimeIfNecessary(phaseScope: AbstractPhaseScope<T>) {
if (!isFirstSolverCycle) {
getPhaseIdForStartedPhase(phaseScope).let { phaseScopeId ->
if (phaseScopeId in startTimePreservationPhaseIds) {
restorePhaseStartTime(phaseScope, phaseScopeId)
}
}
}
}
private fun restorePhaseStartTime(phaseScope: AbstractPhaseScope<T>, phaseScopeId: PhaseScopeId) {
phaseScope.startDate = phasesStartDates[phaseScopeId]
?: throw IllegalStateException("No preserved start date for phase scope: $phaseScopeId")
}
private fun incrementSolverStartCount() {
solverStartCount++
}
private fun resetPhaseStartCounters() = startedPhasesCounters.clear()
private fun incrementPhaseStartCount(phaseScope: AbstractPhaseScope<T>) {
getStartedPhaseCounterForPhase(phaseScope).incrementAndGet()
}
private fun getStartedPhaseCounterForPhase(phaseScope: AbstractPhaseScope<T>): AtomicInteger {
return startedPhasesCounters.getOrPut(phaseScope::class.java) { AtomicInteger(-1) }
}
private fun getPhaseIdForStartedPhase(phaseScope: AbstractPhaseScope<T>): PhaseScopeId {
return PhaseScopeId(phaseScope::class.java, getStartedPhaseCounterForPhase(phaseScope).get())
}
private val phaseLifecycleListener = object : PhaseLifecycleListener<T> {
override fun solvingStarted(solverScope: DefaultSolverScope<T>) = handleSolverStart()
override fun phaseStarted(phaseScope: AbstractPhaseScope<T>) = handlePhaseStarted(phaseScope)
override fun stepStarted(stepScope: AbstractStepScope<T>) {}
override fun solvingEnded(solverScope: DefaultSolverScope<T>) {}
override fun phaseEnded(phaseScope: AbstractPhaseScope<T>) {}
override fun stepEnded(stepScope: AbstractStepScope<T>) {}
}
}

Related

Retrofit 2 - Getting response 200, but list is empty

I've tried to get some json from this api: https://api.cointelegraph.com/api/v1/mobile/feed
I wanna get the "title" field of each news. I created the class that supposed to get this fields and wrapper for this class, but i'm getting an empty list instead of this, i guess it's because i have to get field, which called "data" firstly and then parse it. So i changed my class for getting "title" fields to class for getting "data" field, but i still getting empty list. I guess it's kinda stupid mistake, because it works with github api. I will put some kind of response below and my code. Thanks for trying to help.
Json response:
{"data":{"news":[{"type":"header","data":{"id":22358,"title":"MIT, Stanford Researchers to Fund New \u2018Globally Scalable\u2019 Cryptocurrency, \u2018Unit-e\u2019","lead":"Major U.S. universities including MIT, Stanford and UC Berkeley have teamed up to fund a new digital currency.","thumb":"https:\/\/s3.cointelegraph.com\/storage\/uploads\/view\/f7a7d42a1cba645f861ba810d2524f77.jpg","published_at":{"date":"2019-01-17 18:52:00.000000","timezone_type":3,"timezone":"Europe\/London"},"share_url":"https:\/\/cointelegraph.com\/news\/mit-stanford-researchers-to-fund-new-globally-scalable-cryptocurrency-unit-e","views":2375,"type":"news","author":"Helen Partz","author_id":545,"tag":"Blockchain","badge":{"title":"News","label":"default"},"isSponsored":false,"audio":"https:\/\/s3.cointelegraph.com\/audio\/22358.71732d08-dc28-4787-98b1-46c4e0317916.mp3"}},{"type":"currencies","data":[{"price":3677.37,"name":"BTC","currency":"USD","isIncreased":1,"difference":"0.89"},{"price":123.57,"name":"ETH","currency":"USD","isIncreased":1,"difference":"0.17"},{"price":31.56,"name":"LTC","currency":"USD","isIncreased":1,"difference":"0.19"},{"price":0.33,"name":"XRP","currency":"USD","isIncreased":1,"difference":"1.22"}]},
Interface:
package com.hfad.cointask.service
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.NewsList
import retrofit2.Call
import retrofit2.http.*
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call
}
<b>News class:</b>
<pre>
package com.hfad.cointask.model
class News (private var data: String) {
fun getData():String{
return data
}
}
I've tried to get title in first time like that
package com.hfad.cointask.model
class News (private var title: String) {
fun getTitle():String{
return title
}
}
My wrapper for News class:
package com.hfad.cointask.model
class NewsList {
var list = mutableListOf()
}
My adapter:
package com.hfad.cointask.adapter
import android.support.v7.view.menu.MenuView
import android.support.v7.view.menu.MenuView.ItemView
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.hfad.cointask.R
import com.hfad.cointask.model.News
class CoinAdapter(var values: List): RecyclerView.Adapter() {
override fun onBindViewHolder(p0: CoinViewHolder, p1: Int) {
val itemTitle: News = values[p1]
p0.newsTitle.text = itemTitle.getData()
}
override fun getItemCount(): Int {
return values.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CoinViewHolder {
val view: View = LayoutInflater.from(p0.context).inflate(R.layout.news_item_view, p0, false)
return CoinViewHolder(view)
}
class CoinViewHolder(itemView: View): ViewHolder(itemView) {
var newsTitle: TextView = itemView.findViewById(R.id.item_title)
}
}
And my main activity:
package com.hfad.cointask
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.widget.Toast
import com.hfad.cointask.adapter.CoinAdapter
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.News
import com.hfad.cointask.model.NewsList
import com.hfad.cointask.service.CoinClient
import kotlinx.android.synthetic.main.activity_feed.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class FeedActivity : AppCompatActivity() {
var news = mutableListOf()
private lateinit var newsRecyclerView: RecyclerView
private lateinit var newsAdapter: CoinAdapter
private lateinit var layoutManager: LinearLayoutManager
private val client = mCoinClient().build()
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
message.setText(R.string.title_home)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
message.setText(R.string.title_dashboard)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
message.setText(R.string.title_notifications)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feed)
newsRecyclerView = findViewById(R.id.news_recycler_view)
layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
newsRecyclerView.layoutManager = layoutManager
newsAdapter = CoinAdapter(news)
newsRecyclerView.adapter = newsAdapter
getNews().enqueue(object: Callback{
override fun onResponse(call: Call, response: Response) {
var jsonNews = response.body().list
news.clear()
news.addAll(response.body().list)
newsAdapter.notifyDataSetChanged()
}
override fun onFailure(call: Call?, t: Throwable?) {
val toast = Toast.makeText(this#FeedActivity, t.toString(), Toast.LENGTH_SHORT)
toast.show()
}
})
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
private fun getNews() = client.getFeed()
class mCoinClient {
private val builder = Retrofit
.Builder()
.baseUrl("https://api.cointelegraph.com/")
.addConverterFactory(GsonConverterFactory.create())
private val retrofit: Retrofit by lazy {
builder.build()
}
private val client: CoinClient by lazy {
retrofit.create(CoinClient::class.java)
}
fun build() = client
}
}
Thanks for any help
I think it you should change your Interface to
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call<Your Object that you want to receive it with>
}

Routing key press events to different ui components with TornadoFX

I'm looking for a way to route key press events to the different components depending on a key code. For example there is a textfield to type search string and a tableview with the results. You can enter a keyword and select the search result with the arrow or tab keys WITHOUT switching a focus just from the textfield (or some parent node?).
What is the best way to implement this with TornadoFX?
Thanks in advance!
P.S. The target is to build convenient search bar like KRunner in KDE.
Some code to start with:
import javafx.beans.property.SimpleListProperty
import tornadofx.*
data class Item(
val id: Int,
val name: String
)
class ItemsContext : Controller() {
private val db = listOf(Item(1, "One"), Item(2, "Two"), Item(3, "Three"))
val items = SimpleListProperty<Item>()
init {
items.value = db.observable()
}
fun search(name: String) {
items.value = db.filter { it.name.contains(name, ignoreCase = true) } .observable()
}
}
class QueryView : View() {
private val context: ItemsContext by inject()
override val root = hbox {
textfield {
textProperty().addListener { _, _, newValue ->
context.search(newValue)
}
}
button("done")
}
}
class ResultsView : View() {
private val context: ItemsContext by inject()
override val root = tableview(context.items) {
readonlyColumn("id", Item::id)
readonlyColumn("name", Item::name)
}
}
class AppView : View() {
private val queryView: QueryView by inject()
private val resultsView: ResultsView by inject()
override val root = vbox {
add(queryView)
add(resultsView)
}
}
class AppMain : App(AppView::class)

How to solve this null check in initializer safely?

I'm writing a test case which I need some private properties. Since those private data are generated from a private method, I decided to use reflection to retrieve them after calculation is done. Later I remembered the delegated properties and decide to write a general delegate. Here is the code I got so far:
fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance)
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
lateinit var cache: T // <--- (a)
override opertaor fun getValue(thisRef: Any, property: KProperty<*>): Any? {
#Suppress("UNCHECKED_CAST")
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache
}
}
As you can see, property cache is initialized by getValue calls, and, if initOnce is set, subsequent calls will use that cache instead of keeping calling expensive reflection.
The very unfortunate thing is that at (a) compiler complains because T may be a nullable type and late init mechanism will be broken, but if I initialize it with null and it still complains because T may be a non-null type and null-safety is broken.
Currently, I made this to work by initializing it with the return value of a null-returning Java function. I inspected the bytecode generated and found kotlin compiler placed no null checks for that so it will work for now, but I'm worried that a future kotlin version will have such checks and ruin this trick. How I'm supposed to overcome this?
Now I'm using this, below code is released to public domain. Mention this page if you like, or do nothing.
KTHacks.java
public final class KTHacks {
private KTHacks() {
throw new UnsupportedOperationException();
}
/**
* Forge a null into a platform type and take advantage of relaxed null-checks.
* #param <T>
* #return
*/
public static <T> T NULL() {
return null;
}
}
ReflectBackedProperty.kt
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance)
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
var cache: T = KTHacks.NULL()
override operator fun getValue(thisRef: Any, property: KProperty<*>): T {
#Suppress("UNCHECKED_CAST")
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache
}
}
One way is to limit T to only non-nullable types with an upper bound:
class ReflectBackedProperty<T: Any> : ReadOnlyProperty<Any, T> {}
Another approach is not to bother with lateinit at all:
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
var cache: T? = null
override operator fun getValue(thisRef: Any, property: KProperty<*>): T {
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache as T
}
}

AspectJ: intercept constructor in scala

I'm trying to intercept a constructor parameter in scala with aspectj:
class ConstructorTest extends FlatSpecLike with Matchers {
"MyObjectAspect" should "work" in {
val t = new MyObject("leon")
val result = t.talk()
result should be("LEON")
}
}
class MyObject(text: String) {
def talk(): String = {
println(text)
text
}
}
#Aspect
class MyObjectAspect {
#Around(value = "execution (com.leon.aop.MyObject.new(..))")
def constructCP(jp: ProceedingJoinPoint): AnyRef = {
try {
println("Start...")
val args = jp.getArgs
args(0) = args(0).toString.toUpperCase
jp.proceed(args)
} finally {
println("End...")
}
}
}
It seems not work.
However, a very similar java version could work:
public class AopTest {
#Test
public void test(){
MyJob t = new MyJob("leon");
String result = t.talk();
System.out.println(result);
Assert.assertEquals(result, "LEON");
}
}
public class MyJob {
private String text;
public MyJob(String value){
text = value;
}
public String talk(){
System.out.println(text);
return text;
}
}
#Aspect
public class MyJobAspect {
#Around(value = "execution (com.leon.aop.MyJob.new(..))")
public Object constructCP(ProceedingJoinPoint jp) throws Throwable {
try {
System.out.println("Start..");
Object[] args = jp.getArgs();
args[0] = args[0].toString().toUpperCase();
return jp.proceed(args);
} finally {
System.out.println("End...");
}
}
}
Anyone can help?
Thanks in advance!
Leon
I think I told you before on the AspectJ mailing list: You need to bind arguments you want to modify via args(). Do what I told you on the ML and stop asking the same question everywhere. This is the second time already. Just ignoring my advice and asking again somewhere else does not solve your problem and just creates extra work and extra trouble for those willing to help you.

How to create an instance of a model with the ebean framework and scala in Play 2.2

I would like to instance a model object of the Ebean project with scala and the fremework Play 2.2. I face to an issue with the ID autogenerate and the class parameteres / abstraction :
#Entity
class Task(#Required val label:String) extends Model{
#Id
val id: Long
}
object Task {
var find: Model.Finder[Long, Task] = new Model.Finder[Long, Task](classOf[Long], classOf[Task])
def all(): List[Task] = find.all.asScala.toList
def create(label: String) {
val task = new Task(label)
task.save
}
def delete(id: Long) {
find.ref(id).delete
}
}
The error : "class Task needs to be abstract, since value id is not defined". Any idea to avoid this problem?
I found the solution thanks this link : http://www.avaje.org/topic-137.html
import javax.persistence._
import play.db.ebean._
import play.data.validation.Constraints._
import scala.collection.JavaConverters._
#Entity
#Table( name="Task" )
class Task{
#Id
var id:Int = 0
#Column(name="title")
var label:String = null
}
/**
* Task Data Access Object.
*/
object Task extends Dao(classOf[Task]){
def all(): List[Task] = Task.find.findList().asScala.toList
def create(label: String) {
var task = new Task
task.label = label
Task.save(task)
}
def delete(id: Long) {
Task.delete(id)
}
}
And the DAO :
/**
* Dao for a given Entity bean type.
*/
abstract class Dao[T](cls:Class[T]) {
/**
* Find by Id.
*/
def find(id:Any):T = {
return Ebean.find(cls, id)
}
/**
* Find with expressions and joins etc.
*/
def find():com.avaje.ebean.Query[T] = {
return Ebean.find(cls)
}
/**
* Return a reference.
*/
def ref(id:Any):T = {
return Ebean.getReference(cls, id)
}
/**
* Save (insert or update).
*/
def save(o:Any):Unit = {
Ebean.save(o);
}
/**
* Delete.
*/
def delete(o:Any):Unit = {
Ebean.delete(o);
}

Resources