Skip to content

Commit

Permalink
In the scope of #3068: moves ordering of variables to GamaPopulation
Browse files Browse the repository at this point in the history
In order to make sure it doesnt play any role in the
compilation/validation of TypeDescription
  • Loading branch information
AlexisDrogoul committed Aug 18, 2021
1 parent 0d4f304 commit 318daa3
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 128 deletions.
98 changes: 88 additions & 10 deletions msi.gama.core/src/msi/gama/metamodel/population/GamaPopulation.java
Expand Up @@ -27,10 +27,12 @@
import static msi.gama.common.interfaces.IKeyword.TARGET;
import static msi.gama.common.interfaces.IKeyword.WIDTH;
import static msi.gaml.descriptions.VariableDescription.INIT_DEPENDENCIES_FACETS;
import static msi.gaml.descriptions.VariableDescription.UPDATE_DEPENDENCIES_FACETS;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -39,15 +41,20 @@
import java.util.Set;
import java.util.function.Predicate;

import org.jgrapht.Graphs;
import org.jgrapht.alg.cycle.CycleDetector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import msi.gama.common.geometry.Envelope3D;
import msi.gama.common.interfaces.IKeyword;
import msi.gama.metamodel.agent.IAgent;
import msi.gama.metamodel.agent.IMacroAgent;
import msi.gama.metamodel.shape.GamaPoint;
import msi.gama.metamodel.shape.GamaShape;

import msi.gama.metamodel.shape.IShape;
import msi.gama.metamodel.topology.ITopology;
import msi.gama.metamodel.topology.continuous.ContinuousTopology;
Expand All @@ -72,6 +79,7 @@
import msi.gaml.compilation.IAgentConstructor;
import msi.gaml.descriptions.ActionDescription;
import msi.gaml.descriptions.TypeDescription;
import msi.gaml.descriptions.VariableDescription;
import msi.gaml.expressions.IExpression;
import msi.gaml.operators.Cast;
import msi.gaml.species.ISpecies;
Expand All @@ -81,6 +89,7 @@
import msi.gaml.types.IType;
import msi.gaml.types.Types;
import msi.gaml.variables.IVariable;
import ummisco.gama.dev.utils.DEBUG;

/**
* Written by drogoul Modified on 6 sept. 2010
Expand Down Expand Up @@ -159,28 +168,99 @@ public Object executeOn(final IScope scope) throws GamaRuntimeException {

}

private Iterable<String> getUpdatableAttributeNames(final TypeDescription ecd) {

// June 2020: moving (back) to Iterables instead of Streams.
return Iterables.filter(getOrderedAttributeNames(ecd, UPDATE_DEPENDENCIES_FACETS),
input -> ecd.getAttribute(input).isUpdatable());
}

public Collection<String> getOrderedAttributeNames(final TypeDescription ecd, final Set<String> facetsToConsider) {
// AD Revised in Aug 2019 for Issue #2869: keep constraints between superspecies and subspecies

final DefaultDirectedGraph<String, Object> dependencies = new DefaultDirectedGraph<>(Object.class);
final Map<String, VariableDescription> all = new HashMap<>();
ecd.visitAllAttributes(d -> {
all.put(d.getName(), (VariableDescription) d);
return true;
});

Graphs.addAllVertices(dependencies, all.keySet());
final VariableDescription shape = ecd.getAttribute(SHAPE);
final Collection<VariableDescription> shapeDependencies =
shape == null ? Collections.EMPTY_LIST : shape.getDependencies(facetsToConsider, false, true);

all.forEach((an, var) -> {
for (final VariableDescription newVar : var.getDependencies(facetsToConsider, false, true)) {
final String other = newVar.getName();
// AD Revision in April 2019 for Issue #2624: prevent cycles when building the graph
if (!dependencies.containsEdge(an, other)) {

dependencies.addEdge(other, an);
}
}
// Adding a constraint between the shape of the macrospecies and the populations of microspecies
if (var.isSyntheticSpeciesContainer() && !shapeDependencies.contains(var)) {
dependencies.addEdge(SHAPE, an);
}
});

// TODO: WE HAVE TO FIND A SOLUTION FOR CYCLES IN VARIABLES
// TODO: This method is a performance and memory bottleneck. Too many temp objects created

// June 2021: Temporary patch remove cycles to avoid infinite loop in TopologicalOrderIterator and add variables
// after
Set<String> varToAdd = new HashSet<>();
while (true) {
CycleDetector c = new CycleDetector<>(dependencies);
if (!c.detectCycles()) { break; }
Set<String> cycle = c.findCycles();
DEBUG.OUT("Finding cycles between " + cycle + " in " + this);
for (String s : cycle) {
dependencies.removeVertex(s);
varToAdd.add(s);
break;
}

}

// June 2020: moving (back) to Iterables instead of Streams.
// ArrayList<String> list = Lists.newArrayList((dependencies.vertexSet()));
ArrayList<String> list = Lists.newArrayList(new TopologicalOrderIterator<>(dependencies));

// March 2021: Temporary patch for #3068 - just add missing variables. TopologicalOrderIterator have to be fixed
for (String s : dependencies.vertexSet()) {
if (!list.contains(s)) { list.add(s); }
}
for (String s : varToAdd) {
if (!list.contains(s)) { list.add(s); }
}
return list;
// return StreamEx.of(new TopologicalOrderIterator<>(dependencies)).toList();
}

public GamaPopulation(final IMacroAgent host, final ISpecies species) {
super(0, host == null ? Types.get(EXPERIMENT)
: host.getModel().getDescription().getTypeNamed(species.getName()));
this.host = host;
this.species = species;
final TypeDescription ecd = species.getDescription();
orderedVarNames = ecd.getOrderedAttributeNames(INIT_DEPENDENCIES_FACETS).toArray(new String[0]);
orderedVarNames = getOrderedAttributeNames(ecd, INIT_DEPENDENCIES_FACETS).toArray(new String[0]);
updatableVars =
Iterables.toArray(transform(ecd.getUpdatableAttributeNames(), s -> species.getVar(s)), IVariable.class);
Iterables.toArray(transform(getUpdatableAttributeNames(ecd), s -> species.getVar(s)), IVariable.class);
if (species.isMirror() && host != null) {
mirrorManagement = new MirrorPopulationManagement(species.getFacet(MIRRORS));
} else {
mirrorManagement = null;
}
hashCode = Objects.hash(getSpecies(), getHost());
final boolean[] result = { false, false };
species.getDescription().visitChildren((d) -> {
species.getDescription().visitChildren(d -> {
if (d instanceof ActionDescription && !d.isBuiltIn()) {
final String name = d.getName();
if (name.equals(ISpecies.initActionName)) {
if (ISpecies.initActionName.equals(name)) {
result[0] = true;
} else if (name.equals(ISpecies.stepActionName)) { result[1] = true; }
} else if (ISpecies.stepActionName.equals(name)) { result[1] = true; }
}
return true;
});
Expand Down Expand Up @@ -780,9 +860,7 @@ public boolean hasAgentList() {
@Override
public boolean accept(final IScope scope, final IShape source, final IShape a) {
final IAgent agent = a.getAgent();
if (agent == null) return false;
if (agent.getPopulation() != this) return false;
if (agent.dead()) return false;
if (agent == null || agent.getPopulation() != this || agent.dead()) return false;
final IAgent as = source.getAgent();
if (agent == as) return false;
// }
Expand All @@ -799,7 +877,7 @@ public boolean accept(final IScope scope, final IShape source, final IShape a) {
public void filter(final IScope scope, final IShape source, final Collection<? extends IShape> results) {
final IAgent sourceAgent = source == null ? null : source.getAgent();
results.remove(sourceAgent);
final Predicate<IShape> toRemove = (each) -> {
final Predicate<IShape> toRemove = each -> {
final IAgent a = each.getAgent();
return a == null || a.dead()
|| a.getPopulation() != this
Expand Down
128 changes: 10 additions & 118 deletions msi.gama.core/src/msi/gaml/descriptions/TypeDescription.java
Expand Up @@ -12,29 +12,22 @@

import static msi.gaml.descriptions.VariableDescription.FUNCTION_DEPENDENCIES_FACETS;
import static msi.gaml.descriptions.VariableDescription.INIT_DEPENDENCIES_FACETS;
import static msi.gaml.descriptions.VariableDescription.UPDATE_DEPENDENCIES_FACETS;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.jgrapht.Graphs;
import org.jgrapht.alg.cycle.CycleDetector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;

import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import msi.gama.common.interfaces.IGamlIssue;
import msi.gama.util.GamaMapFactory;
Expand All @@ -43,6 +36,7 @@
import msi.gaml.expressions.types.DenotedActionExpression;
import msi.gaml.statements.Facets;
import msi.gaml.types.IType;
import ummisco.gama.dev.utils.DEBUG;

/**
* A class that represents skills and species (either built-in or introduced by users) The class TypeDescription.
Expand All @@ -54,6 +48,10 @@
@SuppressWarnings ({ "unchecked", "rawtypes" })
public abstract class TypeDescription extends SymbolDescription {

static {
DEBUG.ON();
}

// AD 08/16 : actions and attributes are now inherited dynamically and built
// lazily
protected IMap<String, ActionDescription> actions;
Expand Down Expand Up @@ -262,11 +260,9 @@ public void addOwnAttribute(final VariableDescription vd) {
if (existing != null) {
// A previous definition has been found
// We assert whether their types are compatible or not
if (assertAttributesAreCompatible(existing, vd)) {
markAttributeRedefinition(existing, vd);
vd.copyFrom(existing);
} else
return;
if (!assertAttributesAreCompatible(existing, vd)) return;
markAttributeRedefinition(existing, vd);
vd.copyFrom(existing);
}

addAttributeNoCheck(vd);
Expand All @@ -287,80 +283,6 @@ public void addInheritedAttribute(final VariableDescription vd) {
}
}

public Iterable<String> getUpdatableAttributeNames() {

// June 2020: moving (back) to Iterables instead of Streams.
return Iterables.filter(getOrderedAttributeNames(UPDATE_DEPENDENCIES_FACETS),
input -> getAttribute(input).isUpdatable());
// final Collection<String> vars = getOrderedAttributeNames(UPDATE_DEPENDENCIES_FACETS);
// return StreamEx.of(vars).filter(input -> getAttribute(input).isUpdatable()).toList();
}

public Collection<String> getOrderedAttributeNames(final Set<String> facetsToConsider) {
// AD Revised in Aug 2019 for Issue #2869: keep constraints between superspecies and subspecies

final DefaultDirectedGraph<String, Object> dependencies = new DefaultDirectedGraph<>(Object.class);
final Map<String, VariableDescription> all = new HashMap<>();
this.visitAllAttributes(d -> {
all.put(d.getName(), (VariableDescription) d);
return true;
});

Graphs.addAllVertices(dependencies, all.keySet());
final VariableDescription shape = getAttribute(SHAPE);
final Collection<VariableDescription> shapeDependencies =
shape == null ? Collections.EMPTY_LIST : shape.getDependencies(facetsToConsider, false, true);

all.forEach((an, var) -> {
for (final VariableDescription newVar : var.getDependencies(facetsToConsider, false, true)) {
final String other = newVar.getName();
// AD Revision in April 2019 for Issue #2624: prevent cycles when building the graph
if (!dependencies.containsEdge(an, other)) {

dependencies.addEdge(other, an);
}
}
// Adding a constraint between the shape of the macrospecies and the populations of microspecies
if (var.isSyntheticSpeciesContainer() && !shapeDependencies.contains(var)) {
dependencies.addEdge(SHAPE, an);
}
});

// TODO: WE HAVE TO FIND A SOLUTION FOR CYCLES IN VARIABLES

// June 2021: Temporary patch remove cycles to avoid infinite loop in TopologicalOrderIterator and add variables
// after
Set<String> varToAdd = new HashSet<>();
while (true) {
CycleDetector c = new CycleDetector<>(dependencies);
if (c.detectCycles()) {
Set<String> cycle = c.findCycles();
for (String s : cycle) {
dependencies.removeVertex(s);
varToAdd.add(s);
break;
}
} else {
break;
}

}

// June 2020: moving (back) to Iterables instead of Streams.
// ArrayList<String> list = Lists.newArrayList((dependencies.vertexSet()));
ArrayList<String> list = Lists.newArrayList(new TopologicalOrderIterator<>(dependencies));

// March 2021: Temporary patch for #3068 - just add missing variables. TopologicalOrderIterator have to be fixed
for (String s : dependencies.vertexSet()) {
if (!list.contains(s)) { list.add(s); }
}
for (String s : varToAdd) {
if (!list.contains(s)) { list.add(s); }
}
return list;
// return StreamEx.of(new TopologicalOrderIterator<>(dependencies)).toList();
}

/**
*
*
Expand Down Expand Up @@ -573,7 +495,7 @@ protected void inheritActionsFrom(final TypeDescription p) {
final String actionName = inheritedAction.getName();
final ActionDescription userDeclared = actions == null ? null : actions.get(actionName);
if (userDeclared != null) {
if ((!inheritedAction.isBuiltIn() || !userDeclared.isBuiltIn())) {
if (!inheritedAction.isBuiltIn() || !userDeclared.isBuiltIn()) {
TypeDescription.assertActionsAreCompatible(userDeclared, inheritedAction,
inheritedAction.getOriginName());
if (inheritedAction.isBuiltIn()) {
Expand Down Expand Up @@ -662,36 +584,6 @@ public static void assertActionsAreCompatible(final ActionDescription myAction,
+ " is not compatible with that in the definition of " + actionName + " in " + parentName;
myAction.error(error, IGamlIssue.DIFFERENT_ARGUMENTS, myAction.getUnderlyingElement());
}

// final Map<String, IType<?>> myMap = StreamEx.of(myArgs.iterator()).toMap(d -> d.getName(), d -> d.getType());
// final Map<String, IType<?>> parentMap =
// StreamEx.of(parentArgs.iterator()).toMap(d -> d.getName(), d -> d.getType());
//
// final List<String> myNames = myAction.getArgNames();
// final List<String> parentNames = parentAction.getArgNames();
// boolean different = myNames.size() != parentNames.size();
// if (different) {
// final String error = "The number of arguments should be identical to that of the definition of "
// + actionName + " in " + parentName + ": " + parentNames + "";
// myAction.error(error, IGamlIssue.DIFFERENT_ARGUMENTS, myAction.getUnderlyingElement(null));
// return;
// }
// different = !myNames.containsAll(parentNames);
// if (different) {
// final String error = "The names of arguments should be identical to those of the definition of "
// + actionName + " in " + parentName + " " + parentNames + "";
// myAction.error(error, IGamlIssue.DIFFERENT_ARGUMENTS, myAction.getUnderlyingElement(null));
// return;
// }
// final List<IType<?>> myTypes = myAction.getArgTypes();
// final List<IType<?>> parentTypes = parentAction.getArgTypes();
// different = !myTypes.containsAll(parentTypes);
// if (different) {
// final String error = "The types of arguments should be identical to those in the definition of "
// + actionName + " in " + parentName + " " + parentTypes + "";
// myAction.error(error, IGamlIssue.DIFFERENT_ARGUMENTS, myAction.getUnderlyingElement(null));
// }

}

@Override
Expand All @@ -718,7 +610,7 @@ public boolean visitOwnChildrenRecursively(final DescriptionVisitor<IDescription
}

public boolean visitAllAttributes(final DescriptionVisitor<IDescription> visitor) {
if ((parent != null && parent != this) && !parent.visitAllAttributes(visitor)) return false;
if (parent != null && parent != this && !parent.visitAllAttributes(visitor)) return false;
return visitOwnAttributes(visitor);
}

Expand Down

0 comments on commit 318daa3

Please sign in to comment.