|
|
|
/**
|
|
|
|
* Engine.java
|
|
|
|
* Copyright 2009 by Michael Peter Christen, Frankfurt a. M., Germany
|
|
|
|
* First published 03.12.2009 at http://yacy.net
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program in the file COPYING.LESSER.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package net.yacy.ai.greedy;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.PriorityQueue;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.PriorityBlockingQueue;
|
|
|
|
|
|
|
|
public class Engine<
|
|
|
|
SpecificRole extends Role,
|
|
|
|
SpecificFinding extends Finding<SpecificRole>,
|
|
|
|
SpecificModel extends Model<SpecificRole, SpecificFinding>
|
|
|
|
> {
|
|
|
|
|
|
|
|
protected final PriorityBlockingQueue<Agent<SpecificRole, SpecificFinding, SpecificModel>> agentQueue;
|
|
|
|
protected final PriorityBlockingQueue<Challenge<SpecificRole, SpecificFinding, SpecificModel>> challengeQueue;
|
|
|
|
protected final Agent<SpecificRole, SpecificFinding, SpecificModel> poisonAgent;
|
|
|
|
protected final Challenge<SpecificRole, SpecificFinding, SpecificModel> poisonChallenge;
|
|
|
|
protected final ConcurrentHashMap<SpecificModel, List<SpecificFinding>> settings;
|
|
|
|
protected final ConcurrentHashMap<Asset<SpecificRole, SpecificFinding, SpecificModel>, SpecificModel> assets;
|
|
|
|
private final int cores;
|
|
|
|
|
|
|
|
|
|
|
|
public Engine(int cores) {
|
|
|
|
this.cores = cores;
|
|
|
|
this.poisonAgent = new Agent<SpecificRole, SpecificFinding, SpecificModel>();
|
|
|
|
this.poisonChallenge = new Challenge<SpecificRole, SpecificFinding, SpecificModel>();
|
|
|
|
this.agentQueue = new PriorityBlockingQueue<Agent<SpecificRole, SpecificFinding, SpecificModel>>();
|
|
|
|
this.challengeQueue = new PriorityBlockingQueue<Challenge<SpecificRole, SpecificFinding, SpecificModel>>();
|
|
|
|
this.settings = new ConcurrentHashMap<SpecificModel, List<SpecificFinding>>();
|
|
|
|
this.assets = new ConcurrentHashMap<Asset<SpecificRole, SpecificFinding, SpecificModel>, SpecificModel>();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void start() {
|
|
|
|
int c = (this.cores == 0) ? Runtime.getRuntime().availableProcessors() : this.cores;
|
|
|
|
for (int i = 0; i < c; i++) {
|
|
|
|
new SettingRunner().start();
|
|
|
|
new AssetRunner().start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void stop() {
|
|
|
|
int c = (this.cores == 0) ? Runtime.getRuntime().availableProcessors() : this.cores;
|
|
|
|
for (int i = 0; i < c; i++) {
|
|
|
|
this.agentQueue.put(this.poisonAgent);
|
|
|
|
this.challengeQueue.put(this.poisonChallenge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void inject(Agent<SpecificRole, SpecificFinding, SpecificModel> agent) {
|
|
|
|
agent.getContext().reset();
|
|
|
|
this.agentQueue.put(agent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class SettingRunner extends Thread {
|
|
|
|
public void run() {
|
|
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> agent;
|
|
|
|
Challenge<SpecificRole, SpecificFinding, SpecificModel> challenge;
|
|
|
|
Context<SpecificRole, SpecificFinding, SpecificModel> context;
|
|
|
|
SpecificModel model;
|
|
|
|
List<SpecificFinding> findings;
|
|
|
|
PriorityQueue<Challenge<SpecificRole, SpecificFinding, SpecificModel>> preChallenge = new PriorityQueue<Challenge<SpecificRole, SpecificFinding, SpecificModel>>();
|
|
|
|
try {
|
|
|
|
while ((agent = agentQueue.take()) != poisonAgent) {
|
|
|
|
// check termination of that goal
|
|
|
|
context = agent.getContext();
|
|
|
|
if (context.isCompleted()) continue;
|
|
|
|
|
|
|
|
// produce findings in a setting environment and try to get it from a cache
|
|
|
|
model = agent.getModel();
|
|
|
|
findings = settings.get(model);
|
|
|
|
if (findings == null) {
|
|
|
|
findings = model.explore();
|
|
|
|
settings.put(model, findings);
|
|
|
|
}
|
|
|
|
|
|
|
|
// branch
|
|
|
|
for (SpecificFinding finding: findings) {
|
|
|
|
challenge = new Challenge<SpecificRole, SpecificFinding, SpecificModel>(agent, finding);
|
|
|
|
//System.out.println("finding: " + finding.toString() + ", priority: " + finding.getPriority());
|
|
|
|
preChallenge.add(challenge);
|
|
|
|
}
|
|
|
|
challengefeeder: while (!preChallenge.isEmpty()) {
|
|
|
|
if (context.isCompleted()) break challengefeeder;
|
|
|
|
challengeQueue.put(preChallenge.poll());
|
|
|
|
agent.incInstances();
|
|
|
|
}
|
|
|
|
//while (!challengeQueue.isEmpty()) System.out.println("finding: " + challengeQueue.take().getChallenge().getFinding().toString());
|
|
|
|
}
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class AssetRunner extends Thread {
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public void run() {
|
|
|
|
Challenge<SpecificRole, SpecificFinding, SpecificModel> challenge;
|
|
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> agent, nextAgent;
|
|
|
|
Goal<SpecificRole, SpecificFinding, SpecificModel> goal;
|
|
|
|
Context<SpecificRole, SpecificFinding, SpecificModel> context;
|
|
|
|
Asset<SpecificRole, SpecificFinding, SpecificModel> asset;
|
|
|
|
SpecificRole role;
|
|
|
|
SpecificModel nextModel = null;
|
|
|
|
try {
|
|
|
|
while ((challenge = challengeQueue.take()) != poisonChallenge) {
|
|
|
|
assert challenge != null;
|
|
|
|
agent = challenge.getAgent();
|
|
|
|
agent.decInstances();
|
|
|
|
context = agent.getContext();
|
|
|
|
goal = context.getGoal();
|
|
|
|
role = challenge.getFinding().getRole();
|
|
|
|
|
|
|
|
// check termination by catching the global termination signal
|
|
|
|
// and check expiration before applying finding
|
|
|
|
// this shall not place the model to the results because
|
|
|
|
// it has not the last finding assigned to the current user
|
|
|
|
|
|
|
|
if (context.isCompleted()) continue;
|
|
|
|
|
|
|
|
String debug = agent.getModel().toString() + "\nwill apply " + challenge.getFinding().toString();
|
|
|
|
System.out.println(debug);
|
|
|
|
if (debug.equals("[],[3, 2],[1]\nwill apply 2 -> 1")) {
|
|
|
|
System.out.println("one more please");
|
|
|
|
}
|
|
|
|
// apply finding: compute next model
|
|
|
|
// avoid double computation of findings using cached assets
|
|
|
|
if (context.useAssetCache()) {
|
|
|
|
asset = new Asset<SpecificRole, SpecificFinding, SpecificModel>(agent.getModel(), challenge.getFinding());
|
|
|
|
nextModel = assets.get(asset);
|
|
|
|
if (nextModel == null) {
|
|
|
|
// generate model clone and apply finding
|
|
|
|
try {
|
|
|
|
nextModel = (SpecificModel) agent.getModel().clone();
|
|
|
|
} catch (CloneNotSupportedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
nextModel.applyFinding(challenge.getFinding());
|
|
|
|
nextModel.nextRole();
|
|
|
|
if (context.feedAssetCache()) assets.put(asset, nextModel);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// generate model clone and apply finding
|
|
|
|
try {
|
|
|
|
nextModel = (SpecificModel) agent.getModel().clone();
|
|
|
|
} catch (CloneNotSupportedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
nextModel.applyFinding(challenge.getFinding());
|
|
|
|
nextModel.nextRole();
|
|
|
|
if (context.feedAssetCache()) {
|
|
|
|
asset = new Asset<SpecificRole, SpecificFinding, SpecificModel>(agent.getModel(), challenge.getFinding());
|
|
|
|
assets.put(asset, nextModel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// prune double-occurring models:
|
|
|
|
// while it appears not to be a very good idea to produce models from the
|
|
|
|
// asset cache above and then prune with double appearing models here, this is
|
|
|
|
// still useful if the assets come from an earlier injection of an other agent.
|
|
|
|
// Because the asset cache is a global cache owned by the engine it can produce
|
|
|
|
// ready-computed models that not already exist in the agent cache
|
|
|
|
if (agent.getContext().isKnownModel(nextModel)) {
|
|
|
|
agent.checkInstanceCount();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// place new model into agent and record finding
|
|
|
|
nextAgent = new Agent<SpecificRole, SpecificFinding, SpecificModel>(agent, nextModel, challenge.getFinding());
|
|
|
|
nextAgent.checkInstanceCount();
|
|
|
|
|
|
|
|
// check if we arrived at a termination point
|
|
|
|
SpecificRole terminationRole = nextModel.isTermination();
|
|
|
|
if (terminationRole != null) {
|
|
|
|
// the current role has a termination situation. In case that it is the start user, add a result
|
|
|
|
nextAgent.addResult();
|
|
|
|
if (goal.isFulfilled(nextAgent.getModel())) nextAgent.getContext().announceCompletion();
|
|
|
|
// one of the roles has terminated.
|
|
|
|
// prune this branch for other branches from the parent
|
|
|
|
//System.out.println("terminationRole = " + terminationRole);
|
|
|
|
if (agent.getFinding() == null) {
|
|
|
|
// this is the root of the search tree: a fail in the search
|
|
|
|
//agent.getContext().getGoal().announceFullfillment();
|
|
|
|
} else {
|
|
|
|
assert agent.getFinding() != null;
|
|
|
|
assert agent.getFinding().getRole() != null;
|
|
|
|
agent.setFindingFail();
|
|
|
|
}
|
|
|
|
//System.out.println("found winner model for " + terminationRole.toString() + ", latest finding: " + challenge.getFinding().toString() + "\n" + nextModel.toString());
|
|
|
|
agent.checkInstanceCount();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check time-out for snapshot
|
|
|
|
if (nextAgent.getContext().isSnapshotTimeout()) {
|
|
|
|
nextAgent.addResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
// check pruning
|
|
|
|
|
|
|
|
if (goal.pruning(nextModel)) {
|
|
|
|
agent.checkInstanceCount();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not follow situations where it is known that somebody has made a fatal move in the past
|
|
|
|
if (agent.isPrunedByTerminationInHistory() != null) {
|
|
|
|
agent.checkInstanceCount();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check best move criteria
|
|
|
|
int ranking = agent.getRanking(role);
|
|
|
|
if (context.setBestMove(role, ranking)) {
|
|
|
|
nextAgent.addResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
// check snapshot
|
|
|
|
if (goal.isSnapshot(nextModel)) {
|
|
|
|
nextAgent.addResult();
|
|
|
|
// no pruning here
|
|
|
|
}
|
|
|
|
|
|
|
|
if (context.hasNoResults()) nextAgent.addResult();
|
|
|
|
if (context.isCompleted()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// stack agent for next loop
|
|
|
|
// using the priority of the next role
|
|
|
|
agentQueue.put(nextAgent);
|
|
|
|
}
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|