Skip to content

Commit df4b9ce

Browse files
committed
introduce numerical data evaluation as circumstance and exposed data as info channel for perception
1 parent 31cd6fd commit df4b9ce

19 files changed

Lines changed: 806 additions & 69 deletions

src/main/java/autocompchem/io/ACCJson.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import autocompchem.io.jsonableatomcontainer.AtomContainerSetSerializer;
2828
import autocompchem.io.jsonableatomcontainer.IAtomContainerDeserializer;
2929
import autocompchem.io.jsonableatomcontainer.IAtomContainerSerializer;
30+
import autocompchem.perception.circumstance.AssessData;
31+
import autocompchem.perception.circumstance.AssessData.AssessDataDeserializer;
32+
import autocompchem.perception.circumstance.AssessData.AssessDataSerializer;
3033
import autocompchem.perception.circumstance.CountTextMatches;
3134
import autocompchem.perception.circumstance.CountTextMatches.CountTextMatchesDeserializer;
3235
import autocompchem.perception.circumstance.CountTextMatches.CountTextMatchesSerializer;
@@ -230,6 +233,8 @@ private ACCJson()
230233
new MatchDirComponentSerializer())
231234
.registerTypeAdapter(CountTextMatches.class,
232235
new CountTextMatchesSerializer())
236+
.registerTypeAdapter(AssessData.class,
237+
new AssessDataSerializer())
233238
.registerTypeHierarchyAdapter(IAtomContainer.class,
234239
new IAtomContainerSerializer())
235240
.registerTypeHierarchyAdapter(AtomContainerSet.class,
@@ -279,6 +284,8 @@ private ACCJson()
279284
new CountTextMatchesDeserializer())
280285
.registerTypeAdapter(MatchDirComponent.class,
281286
new MatchDirComponentDeserializer())
287+
.registerTypeAdapter(AssessData.class,
288+
new AssessDataDeserializer())
282289
.registerTypeAdapter(AddDirectiveComponent.class,
283290
new AddDirectiveComponentDeserializer())
284291
.registerTypeAdapter(SetDirectiveComponent.class,
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
package autocompchem.perception.circumstance;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.Objects;
5+
import java.util.TreeMap;
6+
7+
import com.google.gson.JsonDeserializationContext;
8+
import com.google.gson.JsonDeserializer;
9+
import com.google.gson.JsonElement;
10+
import com.google.gson.JsonObject;
11+
import com.google.gson.JsonParseException;
12+
import com.google.gson.JsonSerializationContext;
13+
import com.google.gson.JsonSerializer;
14+
15+
import autocompchem.perception.infochannel.DataAsSource;
16+
import autocompchem.perception.infochannel.InfoChannel;
17+
import autocompchem.perception.infochannel.InfoChannelType;
18+
import autocompchem.utils.StringUtils;
19+
20+
/**
21+
* Condition satisfied if data respects some specific criterion.
22+
* <p>The location of the data to assess is defined
23+
* by a given data path, which is a comma-separated list of strings that identify how
24+
* to navigate the data structure of the {@link NamedDataCollector} identified by the
25+
* {@link DataAsSource} info channel. Only info channels pointing to the same
26+
* data path as this {@link Circumstance} will be considered for assessment.
27+
* </p>
28+
* <p>The criterion for the assessment can be defined in the following ways:<ul>
29+
*
30+
* <li><b>Expression Language (EL)</b>: using an expression formulated according to the
31+
* Java Expression Language, i.e., "${...}" to define the content of the
32+
* expression to be evaluated, and using placeholder "DATA", which is replaced by the
33+
* value of the data fetched from the {@link DataAsSource} info channel.
34+
* For example, the equation "${DATA > 0}" will be evaluated to <code>true</code> if the data
35+
* value is greater than 0, and to <code>false</code> otherwise.</li>
36+
*
37+
* </ul></p>
38+
*
39+
* <p>The assessment is considered satisfied if the equation evaluates to true.
40+
* The negation flag can be used to invert the assessment, i.e., to consider the
41+
* assessment satisfied if the equation evaluates to false.
42+
* </p>
43+
*/
44+
45+
public class AssessData extends Circumstance implements IScoring
46+
{
47+
/**
48+
* Data path to assess.
49+
*/
50+
private final String dataPath;
51+
52+
/**
53+
* Criteria to assess the data against.
54+
*/
55+
private final String criteria;
56+
57+
/**
58+
* Negation flag: if true then the condition is negated
59+
*/
60+
protected final boolean negation;
61+
62+
//------------------------------------------------------------------------------
63+
64+
/**
65+
* Constructs a AssessData defining the data path to assess and the criteria
66+
* to assess it against.
67+
* @param dataPath the data path to assess.
68+
* @param criteria the criteria to assess the data against.
69+
*/
70+
71+
public AssessData(String dataPath, String criteria)
72+
{
73+
this(dataPath, criteria, false);
74+
}
75+
76+
//------------------------------------------------------------------------------
77+
78+
/**
79+
* Constructs a AssessData.
80+
* @param dataPath the data path to assess.
81+
* @param criteria the criteria to assess the data against.
82+
* @param negation if true the condition is satisfied if the criteria is
83+
* not met.
84+
*/
85+
86+
public AssessData(String dataPath, String criteria, boolean negation)
87+
{
88+
super(InfoChannelType.DATA);
89+
this.dataPath = dataPath;
90+
this.criteria = criteria;
91+
this.negation = negation;
92+
}
93+
94+
//------------------------------------------------------------------------------
95+
96+
/**
97+
* Return the data path
98+
* @return the data path
99+
*/
100+
public String getDataPath()
101+
{
102+
return dataPath;
103+
}
104+
105+
//------------------------------------------------------------------------------
106+
107+
/**
108+
* Assess the data against the criteria.
109+
* @param data the data to assess.
110+
* @return true if the data satisfies the criteria, false otherwise.
111+
*/
112+
public boolean assessData(Object data)
113+
{
114+
String result = criteria.replace("DATA", data.toString());
115+
result = StringUtils.evaluateEmbeddedExpressionsInString(result);
116+
return Boolean.parseBoolean(result);
117+
}
118+
119+
//------------------------------------------------------------------------------
120+
121+
/**
122+
* Calculate the satisfaction score. A real value between 0.0 and 1.0
123+
* where 0.0 means "conditions not satisfied" and 1.0 means
124+
* "condition fully satisfied".
125+
* @param data the {@link InfoChannel} providing the data to assess.
126+
* @return numerical score.
127+
*/
128+
129+
@Override
130+
public double calculateScore(InfoChannel input)
131+
{
132+
if (input.getClass() != DataAsSource.class)
133+
return 0;
134+
DataAsSource das = (DataAsSource) input;
135+
136+
if (!das.getDataPath().equals(dataPath))
137+
return 0;
138+
139+
double score = 0.0;
140+
boolean assessment = assessData(das.getData());
141+
142+
if ((negation && !assessment) || (!negation && assessment))
143+
{
144+
score = 1.0;
145+
}
146+
return score;
147+
}
148+
149+
//------------------------------------------------------------------------------
150+
151+
/**
152+
* Return a human readable representation
153+
* @return a string
154+
*/
155+
156+
@Override
157+
public String toString()
158+
{
159+
StringBuilder sb = new StringBuilder();
160+
sb.append(this.getClass().getSimpleName());
161+
sb.append(" [dataPath:").append(dataPath);
162+
sb.append("; criteria:").append(criteria);
163+
sb.append("; negation:").append(negation);
164+
sb.append("]]");
165+
return sb.toString();
166+
}
167+
168+
//------------------------------------------------------------------------------
169+
170+
@Override
171+
public TreeMap<String, JsonElement> getJsonMembers(
172+
JsonSerializationContext context)
173+
{
174+
TreeMap<String, JsonElement> map = new TreeMap<String, JsonElement>();
175+
map.putAll(super.getJsonMembers(context));
176+
map.put("dataPath", context.serialize(dataPath));
177+
map.put("criteria", context.serialize(criteria));
178+
if (negation)
179+
map.put("negation", context.serialize(negation));
180+
return map;
181+
}
182+
183+
//------------------------------------------------------------------------------
184+
185+
public static class AssessDataSerializer
186+
implements JsonSerializer<AssessData>
187+
{
188+
@Override
189+
public JsonElement serialize(AssessData src, Type typeOfSrc,
190+
JsonSerializationContext context)
191+
{
192+
return ICircumstance.getJsonObject(src, context);
193+
}
194+
}
195+
196+
//------------------------------------------------------------------------------
197+
198+
public static class AssessDataDeserializer
199+
implements JsonDeserializer<AssessData>
200+
{
201+
@Override
202+
public AssessData deserialize(JsonElement json,
203+
Type typeOfT, JsonDeserializationContext context)
204+
throws JsonParseException
205+
{
206+
JsonObject jsonObject = json.getAsJsonObject();
207+
208+
String dataPath = jsonObject.get("dataPath").getAsString();
209+
String criteria = jsonObject.get("criteria").getAsString();
210+
boolean negation = false;
211+
if (jsonObject.has("negation"))
212+
{
213+
negation = context.deserialize(jsonObject.get("negation"),
214+
Boolean.class);
215+
}
216+
return new AssessData(dataPath, criteria, negation);
217+
}
218+
}
219+
220+
//------------------------------------------------------------------------------
221+
222+
@Override
223+
public boolean equals(Object o)
224+
{
225+
if (o == null)
226+
return false;
227+
228+
if (o == this)
229+
return true;
230+
231+
if (o.getClass() != getClass())
232+
return false;
233+
234+
AssessData other = (AssessData) o;
235+
236+
if (!this.dataPath.equals(other.dataPath))
237+
return false;
238+
239+
if (!this.criteria.equals(other.criteria))
240+
return false;
241+
242+
if ((this.negation && !other.negation)
243+
|| (!this.negation && other.negation))
244+
return false;
245+
246+
return super.equals(other);
247+
}
248+
249+
//-----------------------------------------------------------------------------
250+
251+
@Override
252+
public int hashCode()
253+
{
254+
return Objects.hash(dataPath, criteria, negation, super.hashCode());
255+
}
256+
257+
//------------------------------------------------------------------------------
258+
259+
}

src/main/java/autocompchem/perception/circumstance/ICircumstance.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ public ICircumstance deserialize(JsonElement json,
125125
case "MatchDirComponent":
126126
result = context.deserialize(jsonObject, MatchDirComponent.class);
127127
break;
128+
129+
case "AssessData":
130+
result = context.deserialize(jsonObject, AssessData.class);
131+
break;
128132

129133
//TODO: this needs to include all the classes implementing this interface
130134
// or search for class in classpath.

src/main/java/autocompchem/perception/circumstance/IScoring.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121

2222

2323
/**
24-
* Interface for circumstance that are able to score
25-
*
26-
* @author Marco Foscato
24+
* Interface for {@link ICircumstance} that are able to score an {@link InfoChannel}
25+
* directly, i.e., without relying on preliminary processing of the i
26+
* {@link InfoChannel}.
2727
*/
2828

2929
public abstract interface IScoring

0 commit comments

Comments
 (0)