You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
9.7 KiB
268 lines
9.7 KiB
/**
|
|
* Agent.java
|
|
* Copyright 2009 by Michael Peter Christen, Frankfurt a. M., Germany
|
|
* First published 03.12.2009 at http://yacy.net
|
|
*
|
|
* $LastChangedDate$
|
|
* $LastChangedRevision$
|
|
* $LastChangedBy$
|
|
*
|
|
* This library 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 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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 lgpl21.txt
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.yacy.ai.greedy;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* the greedy agent
|
|
* this is the main object that contains all elements of a greedy computation
|
|
* To use an agent, one must define
|
|
* - a Model (for the problem that shall be solved) and
|
|
* - a Goal (specifies when an Agent reached a solution for the problem)
|
|
* Then instances of the Model and the Goal must be handed over to the Agent,
|
|
* and the Agent must be feeded into the Engine.
|
|
* The Engine solves the problem and returns the solution as sequence of Findings
|
|
* in this object instance that can be retrieved with takeBestResult()
|
|
*
|
|
* @param <SpecificRole>
|
|
* @param <SpecificFinding>
|
|
* @param <SpecificModel>
|
|
*/
|
|
public class Agent<
|
|
SpecificRole extends Role,
|
|
SpecificFinding extends Finding<SpecificRole>,
|
|
SpecificModel extends Model<SpecificRole, SpecificFinding>
|
|
> implements
|
|
Comparator<Agent<SpecificRole, SpecificFinding, SpecificModel>>,
|
|
Comparable<Agent<SpecificRole, SpecificFinding, SpecificModel>> {
|
|
|
|
private final Context<SpecificRole, SpecificFinding, SpecificModel> context;
|
|
private final SpecificModel model;
|
|
private final SpecificFinding finding;
|
|
private boolean findingFail; // a flag set by child nodes that signal that a specific role has terminated the branch below the current node
|
|
private final Agent<SpecificRole, SpecificFinding, SpecificModel> parentAgent; // the next parent node
|
|
private final int pathlength;
|
|
|
|
/**
|
|
* create a poison agent
|
|
*/
|
|
protected Agent() {
|
|
context = null;
|
|
model = null;
|
|
finding = null;
|
|
findingFail = false;
|
|
parentAgent = null;
|
|
pathlength = 0;
|
|
}
|
|
|
|
public Agent(Context<SpecificRole, SpecificFinding, SpecificModel> context) {
|
|
this.context = context;
|
|
this.model = context.getInitialModel();
|
|
this.finding = null;
|
|
this.findingFail = false;
|
|
this.parentAgent = null;
|
|
this.pathlength = 0;
|
|
}
|
|
|
|
/**
|
|
* Create a clone of the current agent with an loaded finding as attached resource.
|
|
* This is used to branch into alternatives of the given agent configuration.
|
|
* Some elements of the agent must be cloned, other must be extended and one must be referenced without cloning:
|
|
* - challenge: is cloned with an attached finding. This is the actual branching into alternatives
|
|
* - findings: is cloned and extended at the same time. This carries the history of the branch
|
|
* - result: is just referenced because it is used to place a feed-back to the original goal request
|
|
* - initialModel: just referenced
|
|
* - goal: just referenced
|
|
* @param finding
|
|
* @return
|
|
*/
|
|
public Agent(Agent<SpecificRole, SpecificFinding, SpecificModel> parentAgent, SpecificModel newModel, SpecificFinding newFinding) {
|
|
this.parentAgent = parentAgent;
|
|
this.context = parentAgent.context;
|
|
this.model = newModel;
|
|
this.context.addModel(newModel);
|
|
this.finding = newFinding;
|
|
this.findingFail = false;
|
|
this.pathlength = parentAgent.pathlength + 1;
|
|
}
|
|
|
|
public void checkInstanceCount() {
|
|
// in case that there are no agents left, store the current state
|
|
// and fire a shutdown signal
|
|
if (this.context.getInstanceCount() > 0) return;
|
|
//addResult();
|
|
//if (this.getContext().countResults() > 0) this.context.getGoal().announceFullfillment();
|
|
}
|
|
|
|
public void incInstances() {
|
|
this.context.incInstances();
|
|
}
|
|
|
|
public int decInstances() {
|
|
return this.context.decInstances();
|
|
}
|
|
|
|
public Context<SpecificRole, SpecificFinding, SpecificModel> getContext() {
|
|
return this.context;
|
|
}
|
|
|
|
public SpecificModel getModel() {
|
|
return this.model;
|
|
}
|
|
|
|
public Finding<SpecificRole> getFinding() {
|
|
return this.finding;
|
|
}
|
|
|
|
public int getPathLength() {
|
|
return this.pathlength;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public void addResult() {
|
|
// create a challenge that contains the ranking of the current model with the initial role
|
|
// as priority setting attached to the initial move that the user must do to reach the current status
|
|
// find the first move.
|
|
if (this.finding == null) return;
|
|
if (!this.context.getInitialModel().currentRole().equals(this.finding.getRole())) return;
|
|
SpecificFinding finding = null;
|
|
try {
|
|
// because several branches may find the same finding at the root
|
|
// they will attempt to assign different priorities as rankings from the
|
|
// leaf of the search tree. Therefore the findings must be cloned.
|
|
finding = (SpecificFinding) getResultFinding().clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
finding.setPriority(this.model.getRanking(this.pathlength, this.context.initialRole()));
|
|
assert this.finding != null;
|
|
assert this.finding.getRole() != null;
|
|
//System.out.println("finding: " + finding);
|
|
this.context.registerResult(this, finding);
|
|
}
|
|
|
|
|
|
public void setFindingFail() {
|
|
assert this.finding != null;
|
|
assert this.finding.getRole() != null;
|
|
this.findingFail = true;
|
|
}
|
|
|
|
public boolean getFindingFail() {
|
|
return this.findingFail;
|
|
}
|
|
|
|
public SpecificFinding isPrunedByTerminationInHistory() {
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = this;
|
|
while (a != null) {
|
|
if (a.findingFail) return a.finding;
|
|
// step up in the tree
|
|
a = a.parentAgent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public boolean isPrunedByTerminationInHistory(SpecificRole role) {
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = this;
|
|
while (a != null) {
|
|
assert a != null;
|
|
//assert a.finding != null;
|
|
//assert a.finding.getRole() != null;
|
|
if (a.findingFail && a.finding.getRole().equals(role)) return true;
|
|
// step up in the tree
|
|
a = a.parentAgent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public List<SpecificFinding> listPrunedByTerminationInHistory() {
|
|
ArrayList<SpecificFinding> list = new ArrayList<SpecificFinding>(this.pathlength);
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = this;
|
|
while (a != null) {
|
|
if (a.findingFail) list.add(a.finding);
|
|
// step up in the tree
|
|
a = a.parentAgent;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return this.model.hashCode();
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public boolean equals(Object om) {
|
|
if (!(om instanceof Agent)) return false;
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = (Agent<SpecificRole, SpecificFinding, SpecificModel>) om;
|
|
return this.model.equals(a.model);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public SpecificFinding[] getFindings() {
|
|
SpecificFinding[] findings = (SpecificFinding[]) new Finding[this.pathlength];
|
|
int l = this.pathlength - 1;
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = this;
|
|
while (a != null && l >= 0) {
|
|
findings[l--] = a.finding;
|
|
a = a.parentAgent;
|
|
}
|
|
return findings;
|
|
}
|
|
|
|
public SpecificFinding getResultFinding() {
|
|
int l = this.pathlength - 1;
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a = this;
|
|
while (a != null && l >= 0) {
|
|
if (l-- == 0) return a.finding;
|
|
a = a.parentAgent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getRanking(SpecificRole role) {
|
|
return this.model.getRanking(this.pathlength, role);
|
|
}
|
|
|
|
public int compare(
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a1,
|
|
Agent<SpecificRole, SpecificFinding, SpecificModel> a2) {
|
|
|
|
// order of poison agents: they are the largest
|
|
if (a1.context == null) return 1;
|
|
if (a2.context == null) return -1;
|
|
|
|
// by default order by ranking of the model
|
|
SpecificRole role = a1.model.currentRole();
|
|
if (!a2.model.currentRole().equals(role)) return 0;
|
|
int r1 = a1.model.getRanking(a1.pathlength, role);
|
|
int r2 = a2.model.getRanking(a2.pathlength, role);
|
|
|
|
// reverse ordering to get the largest elements at the head of sort queues
|
|
if (r1 < r2) return 1;
|
|
if (r1 > r2) return -1;
|
|
return 0;
|
|
}
|
|
|
|
public int compareTo(Agent<SpecificRole, SpecificFinding, SpecificModel> o) {
|
|
return compare(this, o);
|
|
}
|
|
|
|
}
|