Clingo/ASP: Best way to generate characters in a story generator - answer-set-programming

I am trying to write a story generator in Clingo.
I am trying to say "new characters can be born if existing characters give birth to them." I define new characters as entity(<int\>), which is the best way I could think of to representing entities. I cannot hardcode this as varying number of entities can be created in a story.
My code is :
% Create instances of time, only 3 for testing
time(0..2).
% Arrow of time flows forward
next_t(T, T+1) :- time(T), time(T+1).
% Entity 1 exists at time 0.
entity(1, 0).
% If an entity ever existed, that ID is taken and cannot be assigned to
% other entities
entity_id(ID) :- entity(ID, _).
% If an entity exists, he can give birth to a new entity
% The ID of the new entity will be 1 more than ID of all current entities.
birth(X, Y, T) :- entity(Y, T), X = #max{X1+1:entity_id(X1)}, time(T).
% At each time instant, only 1 entity can be born, as only 1 event can happen per time instant.
% This also should prevent infinite entities to be created.
:- birth(X1, _, T), birth(Y1, _, T), X1!=Y1.
% In case of a birth, create a new entiti the next time instant.
entity(X, T1) :- birth(X, _, T), next(T, T1).
#show entity_id/1.
#show entity/2.
#show birth/3 .
However, output is :
entity_id(1) entity(1,0) birth(2,1,0)
entity(2, 1) is never created, nor are entity(3, 2) or entity(4, 3).
What am I doing wrong? Is there a better way to do this?

You seem to be thinking that ASP statements happen in order from first to last or something like that.
But in fact they're just rules about a collection of atoms. The rules always hold. In particular, the rule:
entity_id(ID) :- entity(ID, _).
says nothing about duplicates. It just says that for every entity which has an ID ID, ID is an entity_id.
If you want to encode the rule that each ID is used once, you should write it as:
:- {entity(ID,_)} > 1; entity_id(ID).
Also you try to construct "ID's" which are "one more than all current entities", but there's no such thing as "current" entities. All we have to guide us are the time-steps.
Let's try writing this in a way that keeps our timesteps explicit throughout.
% Create instances of time, only 3 for testing
time(0..2).
% Entity eve exists at time 0.
entity(1, 0).
nextID(2, 0).
% If an entity existed at the previous time-step, they continue to exist
% at the next time-step (as I understand, no entity dies).
entity(ID, T) :- entity(ID, T-1); time(T).
% Any entity who was alive on the previous time-step can give birth to
% a child at time T. This child's ID is the current `nextID`
% Note this is a _choice_ rule. The entity _can_ give birth, they don't
% have to. Also we only allow at most one of these births to happen at
% each time-step.
{birth(ChildID, ParentID, T) : entity(ParentID,T-1)} <= 1 :- nextID(ChildID,T).
% Once born, an ID becomes an entity.
entity(ID,T) :- birth(ID,_,T).
% If an entity was born at the previous time-step, the nextID increases by one
% for this time-step.
nextID(ID+1,T) :- nextID(ID,T-1); time(T); entity(ID,T-1).
% Otherwise it stays the same.
nextID(ID,T) :- nextID(ID,T-1); time(T); not entity(ID,T-1).
#show birth/3.
Running this I find there are 5 models.
$ clingo entities.asp 0
clingo version 5.3.1
Reading from entities.asp
Solving...
Answer: 1
% ^ Nobody is ever born
Answer: 2
birth(2,1,2)
% ^ Nobody born at time 1. 1 births a child at time 2
Answer: 3
birth(2,1,1) birth(3,2,2)
% ^ 1 births a child at time 1 and that child gives birth at time 2.
Answer: 4
birth(2,1,1)
% ^ 1 births a child at time 1. Nobody is born at time 2.
Answer: 5
birth(2,1,1) birth(3,1,2)
% ^ 1 births two children; one at time 1 and another at time 2.
SATISFIABLE
Models : 5
Calls : 1
Time : 0.011s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time : 0.004s

Related

ASP Clingo - getting the exact count of atoms

I'm looking forward to assign a specific count of persons to a specific shift. For example I got six persons and three different shifts. Now I have to assign exact two persons to every shift. I tried something like this but..
NOTE: this won't work, so please edit as fast as possible to misslead people, I even removed the "." after it so nobody is copying it:
person(a)
person(b)
person(c)
person(d)
person(e)
person(f)
shift("mor")
shift("aft")
shift("nig")
shiftCount(2).
{ assign(P,S) : shift(S)} = 1 :- person(P).
% DO NOT COPY THIS! SEE RIGHT ANSWER DOWN BELOW
:- #count{P : assign(P,"mor")} = K, shiftCount(K).
:- #count{P : assign(P,"aft")} = K, shiftCount(K).
:- #count{P : assign(P,"nig")} = K, shiftCount(K).
#show assign/2.
Is this possible to count the number of assigned shifts, so I can assign exactly as many people as a given number?
The output of the code above (when the "." are inserted) is:
clingo version 5.5.0
Reading from stdin
Solving...
Answer: 1
assign(a,"nig") assign(b,"aft") assign(c,"mor") assign(d,"mor")
assign(e,"mor") assign(f,"mor")
SATISFIABLE
Models : 1+
Calls : 1
Time : 0.021s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time : 0.000s
Here you can defently see, that the morning ("mor") shift is used more than two times, as difined in the shiftCount. What do I need to change to get the wanted result?
Replace your 3 lines constraints with
{assign(P,S): person(P)} == K :- shift(S), shiftCount(K).
or alternatively if you want to use the constraint writing:
:- {assign(P,S): person(P)} != K, shift(S), shiftCount(K).
First line states: For a given shiftCount K and for every shift S: the number of assignments over all people P for this shift S is K.
The constraint reads: it can not be the case for a shiftCount K and a shift S that the number of assignments over all people P to the shift S is not K.
Please do not alter your question / sample code dramatically since this may leads to the case that this answer won't work anymore.

Clingo: logic OR in integrity constraint

For a lecture exercise, I have to represent in Answer Set Programming (we use Clingo as interpreter) a the following integrity constraint:
"You have to plan the calendar of a Masterclass. Normally, the lectures are on Fridays (8 hours) and Saturday(4 or 5 hours). And the 7th and 16th week are full, which means the lectures goes from Monday to Friday, with 8 hours per day, and on Saturday, with 4 or 5 hours of lecture."
The basic settings for the problem are the following:
#const n_weeks = 2. % for now we limit the problem size to 2 weeks
#const n_days = 6. % days in a fullweek
week(1..n_weeks).
day(1..n_days).
hour(1..8). % from the 1st to the 8th hour of the day
% the second week is a fullweek (lectures from 1st to 8th hour from Monday to Friday)
fullweek(2).
% We number all the weekdays (mon-fri) (we need it for the saturday)
fullday(1..5).
% some professors just for test
prof("prof1").
prof("prof2").
prof("prof3").
prof("prof4").
% subj, total hours, prof
subject("subj1", 8, "prof1").
subject("subj2", 14, "prof2").
subject("subj3", 24, "prof3").
subject("subj4", 11, "prof1").
% The main predicate, to print out at the end.
0 {calendar(W, D, H, W*100+D*10+H, lecture(S, P))} 1 :- week(W), day(D), hour(H), subject(S, _, P).
Now, as mentioned above (the final line in bold), we have some problems with the following constraint:
"In this masterclass the hours of a lecture on Saturday can be 4 or 5."
For now, me and my colleagues represented this constraint like this:
% The Saturday has 4 or 5 hours of lecture
:- #count{I : calendar(W, D, _, I, lecture(_, _))} > 5, week(W), day(D), not fullday(D).
:- #count{I : calendar(W, D, _, I, lecture(_, _))} < 4, week(W), day(D), not fullday(D).
Is it the right way to represent constraint like this? There is a better approach?
I do not believe there is the "right way" to represent the constraint as long as it is technically correct. I suggest to consider the following points:
The way on how to express Saturday is complicated, i.e. you can replace the variable D by 6 and eliminate the predicates day and fullday.
I do not understand why you use "lecture(_, _)" instead of the underscore.
I am not sure why you use the variable I for counting and think you like to count the hours instead.
Maybe it make sense to use disjunction explicitly, i.e. use a predicate like "hours_on_sunday(H)" and write a rule that H must be 4 or 5.

Optaplanner Curriculum example, Explanation for curriculumCourseScoreRules.drl

I'm currently reading the course curriculum example of optaplanner and I can't seem to understand this:
rule "conflictingLecturesSameCourseInSamePeriod"
when
// line 1
Lecture($leftId : id, $leftCourse : course, $period : period, period != null)
// line 2
Lecture(course == $leftCourse, period == $period, id > $leftId)
then
scoreHolder.addHardConstraintMatch(kcontext, -1);
end
Questions are:
- What's the difference between the Lecture() in line 1 and the Lecture in line 2?
- I understand the variable assignements are happening in Line 1 but in Line 2, what's the difference between course and $leftCourse, period and $period and id and $leftId
So far, I can't seem to find any explanation in the documentation
A binding such as $leftCourse : course establishes $leftCourse as a variable that refers to the field course of a Lecture object.
The lecture in line 1 is identified by its id; the one in line 2 has an id that is greater. Assuming that id values are primary keys, this combination would basically match all possible unordered pairs of Lectures, but ...
... the constraint course == $leftCourse restricts the pairings to those of identical courses and ...
... the constraint period == $period furthermore restricts it to equal (non-null) periods.
In other words, the planning rules out an assignment for two different Lectures for the same course in the same period.

partial Distance Based RDA - Centroids vanished from Plot

I am trying to fir a partial db-RDA with field.ID to correct for the repeated measurements character of the samples. However including Condition(field.ID) leads to Disappearance of the centroids of the main factor of interest from the plot (left plot below).
The Design: 12 fields have been sampled for species data in two consecutive years, repeatedly. Additionally every year 3 samples from reference fields have been sampled. These three fields have been changed in the second year, due to unavailability of the former fields.
Additionally some environmental variables have been sampled (Nitrogen, Soil moisture, Temperature). Every field has an identifier (field.ID).
Using field.ID as Condition seem to erroneously remove the F1 factor. However using Sampling campaign (SC) as Condition does not. Is the latter the rigth way to correct for repeated measurments in partial db-RDA??
set.seed(1234)
df.exp <- data.frame(field.ID = factor(c(1:12,13,14,15,1:12,16,17,18)),
SC = factor(rep(c(1,2), each=15)),
F1 = factor(rep(rep(c("A","B","C","D","E"),each=3),2)),
Nitrogen = rnorm(30,mean=0.16, sd=0.07),
Temp = rnorm(30,mean=13.5, sd=3.9),
Moist = rnorm(30,mean=19.4, sd=5.8))
df.rsp <- data.frame(Spec1 = rpois(30, 5),
Spec2 = rpois(30,1),
Spec3 = rpois(30,4.5),
Spec4 = rpois(30,3),
Spec5 = rpois(30,7),
Spec6 = rpois(30,7),
Spec7 = rpois(30,5))
data=cbind(df.exp, df.rsp)
dbRDA <- capscale(df.rsp ~ F1 + Nitrogen + Temp + Moist + Condition(SC), df.exp); ordiplot(dbRDA)
dbRDA <- capscale(df.rsp ~ F1 + Nitrogen + Temp + Moist + Condition(field.ID), df.exp); ordiplot(dbRDA)
You partial out variation due to ID and then you try to explain variable aliased to this ID, but it was already partialled out. The key line in the printed output was this:
Some constraints were aliased because they were collinear (redundant)
And indeed, when you ask for details, you get
> alias(dbRDA, names=TRUE)
[1] "F1B" "F1C" "F1D" "F1E"
The F1? variables were constant within ID which already was partialled out, and nothing was left to explain.

How to get nodes that have a given amount of outgoing relationships with a given property in Neo4j Cypher?

In my domain a node can have several relationships of the same type to other entities. Each relationship have several properties and I'd like to retrieve the nodes that are connected by at least 2 relationships that present a given property.
EG: A relationship between nodes have a property year. How do I find the nodes that have at least two outgoing relationships with the year set to 2012?
Why Chypher query so far looks like this (syntax error)
START x = node(*)
MATCH x-[r:RELATIONSHIP_TYPE]->y
WITH COUNT(r.year == 2012) AS years
WHERE HAS(r.year) AND years > 1
RETURN x;
I tried also nesting queries but I believe that it's not allowed in Cypher. The closest thing is the following but I do not know how to get rid of the nodes with value 1:
START n = node(*)
MATCH n-[r:RELATIONSHIP_TYPE]->c
WHERE HAS(r.year) AND r.year == 2012
RETURN n, COUNT(r) AS counter
ORDER BY counter DESC
Try this query
START n = node(*)
MATCH n-[r:RELATIONSHIP_TYPE]->c
WHERE HAS(r.year) AND r.year=2012
WITH n, COUNT(r) AS rc
WHERE rc > 1
RETURN n, rc