![JAR search and dependency download from the Maven repository](/logo.png)
org.ggp.base.util.propnet.architecture.PropNet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alloy-ggp-base Show documentation
Show all versions of alloy-ggp-base Show documentation
A modified version of the GGP-Base library for Alloy.
The newest version!
package org.ggp.base.util.propnet.architecture;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlPool;
import org.ggp.base.util.gdl.grammar.GdlProposition;
import org.ggp.base.util.gdl.grammar.GdlRelation;
import org.ggp.base.util.gdl.grammar.GdlSentence;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.logging.GamerLogger;
import org.ggp.base.util.propnet.architecture.components.And;
import org.ggp.base.util.propnet.architecture.components.Not;
import org.ggp.base.util.propnet.architecture.components.Or;
import org.ggp.base.util.propnet.architecture.components.Proposition;
import org.ggp.base.util.propnet.architecture.components.Transition;
import org.ggp.base.util.statemachine.Role;
import com.google.common.base.Preconditions;
/**
* The PropNet class is designed to represent Propositional Networks.
*
* A propositional network (also known as a "propnet") is a way of representing
* a game as a logic circuit. States of the game are represented by assignments
* of TRUE or FALSE to "base" propositions, each of which represents a single
* fact that can be true about the state of the game. For example, in a game of
* Tic-Tac-Toe, the fact (cell 1 1 x) indicates that the cell (1,1) has an 'x'
* in it. That fact would correspond to a base proposition, which would be set
* to TRUE to indicate that the fact is true in the current state of the game.
* Likewise, the base corresponding to the fact (cell 1 1 o) would be false,
* because in that state of the game there isn't an 'o' in the cell (1,1).
*
* A state of the game is uniquely determined by the assignment of truth values
* to the base propositions in the propositional network. Every assignment of
* truth values to base propositions corresponds to exactly one unique state of
* the game.
*
* Given the values of the base propositions, you can use the connections in
* the network (AND gates, OR gates, NOT gates) to determine the truth values
* of other propositions. For example, you can determine whether the terminal
* proposition is true: if that proposition is true, the game is over when it
* reaches this state. Otherwise, if it is false, the game isn't over. You can
* also determine the value of the goal propositions, which represent facts
* like (goal xplayer 100). If that proposition is true, then that fact is true
* in this state of the game, which means that xplayer has 100 points.
*
* You can also use a propositional network to determine the next state of the
* game, given the current state and the moves for each player. First, you set
* the input propositions which correspond to each move to TRUE. Once that has
* been done, you can determine the truth value of the transitions. Each base
* proposition has a "transition" component going into it. This transition has
* the truth value that its base will take on in the next state of the game.
*
* For further information about propositional networks, see:
*
* "Decomposition of Games for Efficient Reasoning" by Eric Schkufza.
* "Factoring General Games using Propositional Automata" by Evan Cox et al.
*
* @author Sam Schreiber
*/
public final class PropNet implements Serializable
{
private static final long serialVersionUID = 1L;
/** References to every component in the PropNet. */
private final Set components;
/** References to every Proposition in the PropNet. */
private final Set propositions;
/** References to every BaseProposition in the PropNet, indexed by name. */
private final Map basePropositions;
/** References to every InputProposition in the PropNet, indexed by name. */
private final Map inputPropositions;
/** References to every LegalProposition in the PropNet, indexed by role. */
private final Map> legalPropositions;
/** References to every GoalProposition in the PropNet, indexed by role. */
private final Map> goalPropositions;
/** A reference to the single, unique, InitProposition. */
private final Proposition initProposition;
/** A reference to the single, unique, TerminalProposition. */
private final Proposition terminalProposition;
/** A helper mapping between input/legal propositions. */
private final Map legalInputMap;
/** A helper list of all of the roles. */
private final List roles;
/** A helper set of all base propositions in the propnet that are initially true. */
private final Set initiallyTrueBasePropositions;
public void addComponent(Component c)
{
components.add(c);
if (c instanceof Proposition) propositions.add((Proposition)c);
}
/**
* Creates a new PropNet from a list of Components, along with indices over
* those components.
*
* @param components
* A list of Components.
*/
public PropNet(List roles, Set components, Set initiallyTrue)
{
this.roles = roles;
this.components = components;
this.propositions = recordPropositions();
this.basePropositions = recordBasePropositions();
this.inputPropositions = recordInputPropositions();
this.legalPropositions = recordLegalPropositions();
this.goalPropositions = recordGoalPropositions();
this.initProposition = recordInitProposition();
this.terminalProposition = recordTerminalProposition();
this.legalInputMap = makeLegalInputMap();
this.initiallyTrueBasePropositions = initiallyTrue;
}
public List getRoles()
{
return roles;
}
public Map getLegalInputMap()
{
return legalInputMap;
}
private Map makeLegalInputMap() {
Map legalInputMap = new HashMap();
// Create a mapping from Body->Input.
Map, Proposition> inputPropsByBody = new HashMap, Proposition>();
for(Proposition inputProp : inputPropositions.values()) {
List inputPropBody = (inputProp.getName()).getBody();
inputPropsByBody.put(inputPropBody, inputProp);
}
// Use that mapping to map Input->Legal and Legal->Input
// based on having the same Body proposition.
for(Set legalProps : legalPropositions.values()) {
for(Proposition legalProp : legalProps) {
List legalPropBody = (legalProp.getName()).getBody();
if (inputPropsByBody.containsKey(legalPropBody)) {
Proposition inputProp = inputPropsByBody.get(legalPropBody);
legalInputMap.put(inputProp, legalProp);
legalInputMap.put(legalProp, inputProp);
}
}
}
return legalInputMap;
}
/**
* Getter method.
*
* @return References to every BaseProposition in the PropNet, indexed by
* name.
*/
public Map getBasePropositions()
{
return basePropositions;
}
/**
* Getter method.
*
* @return References to every Component in the PropNet.
*/
public Set getComponents()
{
return components;
}
/**
* Getter method.
*
* @return References to every GoalProposition in the PropNet, indexed by
* player name.
*/
public Map> getGoalPropositions()
{
return goalPropositions;
}
/**
* Getter method. A reference to the single, unique, InitProposition.
*
* @return
*/
public Proposition getInitProposition()
{
return initProposition;
}
/**
* Getter method.
*
* @return References to every InputProposition in the PropNet, indexed by
* name.
*/
public Map getInputPropositions()
{
return inputPropositions;
}
/**
* Getter method.
*
* @return References to every LegalProposition in the PropNet, indexed by
* player name.
*/
public Map> getLegalPropositions()
{
return legalPropositions;
}
/**
* Getter method.
*
* @return References to every Proposition in the PropNet.
*/
public Set getPropositions()
{
return propositions;
}
/**
* Getter method.
*
* @return A reference to the single, unique, TerminalProposition.
*/
public Proposition getTerminalProposition()
{
Preconditions.checkNotNull(terminalProposition);
return terminalProposition;
}
/**
* Returns a set of the initially true base propositions, if available.
* This is available from propnets generated by the OptimizingPropNetFactory.
* If not available, returns null.
*/
public Set getInitiallyTrueBasePropositions()
{
return initiallyTrueBasePropositions;
}
/**
* Returns a representation of the PropNet in .dot format.
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("digraph propNet\n{\n");
for ( Component component : components )
{
sb.append("\t" + component.toString() + "\n");
}
sb.append("}");
return sb.toString();
}
/**
* Outputs the propnet in .dot format to a particular file.
* This can be viewed with tools like Graphviz and ZGRViewer.
*
* @param filename the name of the file to output to
*/
public void renderToFile(String filename) {
try {
File f = new File(filename);
try (FileOutputStream fos = new FileOutputStream(f)) {
try (OutputStreamWriter fout = new OutputStreamWriter(fos, "UTF-8")) {
fout.write(toString());
}
}
} catch(Exception e) {
GamerLogger.logStackTrace("StateMachine", e);
}
}
/**
* Builds an index over the BasePropositions in the PropNet.
*
* This is done by going over every single-input proposition in the network,
* and seeing whether or not its input is a transition, which would mean that
* by definition the proposition is a base proposition.
*
* @return An index over the BasePropositions in the PropNet.
*/
private Map recordBasePropositions()
{
Map basePropositions = new HashMap();
for (Proposition proposition : propositions) {
// Skip all propositions without exactly one input.
if (proposition.getInputs().size() != 1)
continue;
Component component = proposition.getSingleInput();
if (component instanceof Transition) {
basePropositions.put(proposition.getName(), proposition);
}
}
return basePropositions;
}
/**
* Builds an index over the GoalPropositions in the PropNet.
*
* This is done by going over every function proposition in the network
* where the name of the function is "goal", and extracting the name of the
* role associated with that goal proposition, and then using those role
* names as keys that map to the goal propositions in the index.
*
* @return An index over the GoalPropositions in the PropNet.
*/
private Map> recordGoalPropositions()
{
Map> goalPropositions = new HashMap>();
for (Proposition proposition : propositions)
{
// Skip all propositions that aren't GdlRelations.
if (!(proposition.getName() instanceof GdlRelation))
continue;
GdlRelation relation = (GdlRelation) proposition.getName();
if (!relation.getName().getValue().equals("goal"))
continue;
Role theRole = new Role((GdlConstant) relation.get(0));
if (!goalPropositions.containsKey(theRole)) {
goalPropositions.put(theRole, new HashSet());
}
goalPropositions.get(theRole).add(proposition);
}
return goalPropositions;
}
/**
* Returns a reference to the single, unique, InitProposition.
*
* @return A reference to the single, unique, InitProposition.
*/
private Proposition recordInitProposition()
{
for (Proposition proposition : propositions)
{
// Skip all propositions that aren't GdlPropositions.
if (!(proposition.getName() instanceof GdlProposition))
continue;
GdlConstant constant = ((GdlProposition) proposition.getName()).getName();
if (constant.getValue().toUpperCase().equals("INIT")) {
return proposition;
}
}
return null;
}
/**
* Builds an index over the InputPropositions in the PropNet.
*
* @return An index over the InputPropositions in the PropNet.
*/
private Map recordInputPropositions()
{
Map inputPropositions = new HashMap();
for (Proposition proposition : propositions)
{
// Skip all propositions that aren't GdlFunctions.
if (!(proposition.getName() instanceof GdlRelation))
continue;
GdlRelation relation = (GdlRelation) proposition.getName();
if (relation.getName().getValue().equals("does")) {
inputPropositions.put(proposition.getName(), proposition);
}
}
return inputPropositions;
}
/**
* Builds an index over the LegalPropositions in the PropNet.
*
* @return An index over the LegalPropositions in the PropNet.
*/
private Map> recordLegalPropositions()
{
Map> legalPropositions = new HashMap>();
for (Proposition proposition : propositions)
{
// Skip all propositions that aren't GdlRelations.
if (!(proposition.getName() instanceof GdlRelation))
continue;
GdlRelation relation = (GdlRelation) proposition.getName();
if (relation.getName().getValue().equals("legal")) {
GdlConstant name = (GdlConstant) relation.get(0);
Role r = new Role(name);
if (!legalPropositions.containsKey(r)) {
legalPropositions.put(r, new HashSet());
}
legalPropositions.get(r).add(proposition);
}
}
return legalPropositions;
}
/**
* Builds an index over the Propositions in the PropNet.
*
* @return An index over Propositions in the PropNet.
*/
private Set recordPropositions()
{
Set propositions = new HashSet();
for (Component component : components)
{
if (component instanceof Proposition) {
propositions.add((Proposition) component);
}
}
return propositions;
}
/**
* Records a reference to the single, unique, TerminalProposition.
*
* @return A reference to the single, unqiue, TerminalProposition.
*/
private Proposition recordTerminalProposition()
{
for ( Proposition proposition : propositions )
{
if ( proposition.getName() instanceof GdlProposition )
{
GdlConstant constant = ((GdlProposition) proposition.getName()).getName();
if ( constant.getValue().equals("terminal") )
{
return proposition;
}
}
}
throw new IllegalArgumentException("The propnet must have a terminal proposition!");
}
public int getSize() {
return components.size();
}
public int getNumAnds() {
int andCount = 0;
for(Component c : components) {
if(c instanceof And)
andCount++;
}
return andCount;
}
public int getNumOrs() {
int orCount = 0;
for(Component c : components) {
if(c instanceof Or)
orCount++;
}
return orCount;
}
public int getNumNots() {
int notCount = 0;
for(Component c : components) {
if(c instanceof Not)
notCount++;
}
return notCount;
}
public int getNumLinks() {
int linkCount = 0;
for(Component c : components) {
linkCount += c.getOutputs().size();
}
return linkCount;
}
/**
* Removes a component from the propnet. Be very careful when using
* this method, as it is not thread-safe. It is highly recommended
* that this method only be used in an optimization period between
* the propnet's creation and its initial use, during which it
* should only be accessed by a single thread.
*
* The INIT and terminal components cannot be removed.
*/
public void removeComponent(Component c) {
//Go through all the collections it could appear in
if(c instanceof Proposition) {
Proposition p = (Proposition) c;
GdlSentence name = p.getName();
if(basePropositions.containsKey(name)) {
basePropositions.remove(name);
} else if(inputPropositions.containsKey(name)) {
inputPropositions.remove(name);
//The map goes both ways...
Proposition partner = legalInputMap.get(p);
if(partner != null) {
legalInputMap.remove(partner);
legalInputMap.remove(p);
}
} else if(name == GdlPool.getProposition(GdlPool.getConstant("INIT"))) {
throw new RuntimeException("The INIT component cannot be removed. Consider leaving it and ignoring it.");
} else if(name == GdlPool.getProposition(GdlPool.TERMINAL)) {
throw new RuntimeException("The terminal component cannot be removed.");
} else {
for(Set propositions : legalPropositions.values()) {
if(propositions.contains(p)) {
propositions.remove(p);
Proposition partner = legalInputMap.get(p);
if(partner != null) {
legalInputMap.remove(partner);
legalInputMap.remove(p);
}
}
}
for(Set propositions : goalPropositions.values()) {
propositions.remove(p);
}
}
propositions.remove(p);
}
components.remove(c);
//Remove all the local links to the component
for(Component parent : c.getInputs())
parent.removeOutput(c);
for(Component child : c.getOutputs())
child.removeInput(c);
//These are actually unnecessary...
//c.removeAllInputs();
//c.removeAllOutputs();
}
public @Nullable Proposition getGoalProposition(Role role, int goalValue) {
String goalValueString = Integer.toString(goalValue);
for (Proposition prop : getGoalPropositions().get(role)) {
if (prop.getName().get(1).toString().equals(goalValueString)) {
return prop;
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy