Remove item from multiple lists - list

I have a subproblem for which the solution is way larger than what I would suspect is necessary.
The problem is defined as follows
Remove X from all groups where group has id between Y and Z or A and B
expressed as a pseudo query it looks like this with Y,Z,A,B set to 0,1,3,4
remove(X,
[ period(0,1), period(3,4) ],
[
group(0, [ subgroup([_,_,X,_,_]), subgroup([X])]),
group(1, [ subgroup([X])]),
group(2, [ subgroup([_,_,X])]),
group(3, [ subgroup([_,X,_])]),
group(4, [ subgroup([X,_,_])])
], UpdatedGroups).
The result will be
UpdatedGroups = [
group(0, [ subgroup([_,_,_,_]), subgroup([])]),
group(1, [ subgroup([])]),
group(2, [ subgroup([_,_,X])]),
group(3, [ subgroup([_,_])]),
group(4, [ subgroup([_,_])])
]
So, my solution to this is:
While start of current period is less than or equal end of current period, do removal of X in groups, while "incrementing" start of day. Repeat until no more periods
The removal of X in groups is done by "looping" all groups and check if it matches the period, and if it does remove the user from subgroups, which again is done by "looping".
This is a very tedious but straight forward solution, now my problem is that I quite often find myself doing stuff like this, and cannot find approaches to do this in a less comprehensive way.
Are there other approaches than mine that does not cover 50+ lines?
Updated
Thanks a lot, the code became so much cleaner - it might go further, but now it is possible to actually post here (this is modified a bit - but the logic is there)
inPeriods(Day, [ period(Start,End) | _ ]) :- between(Start,End, Day).
inPeriods(Day, [ _ | RemainingPeriods ]) :- inPeriods(Day, RemainingPeriods).
ruleGroupsInPeriod(Periods, rulegroup(Day,_)) :- inPeriods(Day, Periods).
removeUserFromRelevantRuleGroups(UserId, Periods, RuleGroups, UpdatedRuleGroups) :-
include(ruleGroupsInPeriod(Periods), RuleGroups, IncludedRuleGroups).
exclude(ruleGroupsInPeriod(Periods), RuleGroups, ExcludedRuleGroups),
maplist(updateRuleGroup(UserId), IncludedRuleGroups, UpdatedIncludedRuleGroups)
append(UpdatedIncludedRuleGroups, ExcludedRuleGroups, UpdatedRuleGroups).
updateRuleGroup(UserId, rulegroup(X, RuleList), rulegroup(X, UpdatedRuleList)) :-
maplist(updateRule(UserId), RuleList, UpdatedRuleList).
updateRule(UserId, rule(X, UserList), rule(X, UpdatedUserList)) :-
delete(UserList, UserId, UpdatedUserList).

Yes.
The pattern you describe is very common, and all serious Prolog systems ship with powerful meta-predicates (i.e., predicates whose arguments denote predicates) that let you easily describe this and many other common situations in a flexible manner, using at most a few simple additional definitions for your concrete relations.
Richard O'Keefe's proposal for An elementary Prolog Library contains the descriptions and implementations of many such predicates, which are becoming increasingly available in all major Prolog implementations and also in the Prologue for Prolog.
In particular, you should study:
include/3
exclude/3
maplist/[2,3]
Note though that many of the described predicates are impure in the sense that they will destroy declarative properties of your code. In contrast to true relations, you won't be able to use them in all directions while preserving logical soundness.
Exercise: Which of the three predicates mentioned above, if any, preserve logical-purity when everything else is pure, and which, if any, do not?

Related

Iterate through list to update particular items in the list

How can I update the values in a list for the indices that are equal to everyone in the selected group agents?:
persons-own [
grouped
flockmates
]
to create-trustConnection
set nrPersons count persons
set vector []
repeat nrPersons [set vector 0.4 vector]
end
to updateVector
let alonePersons (count persons with [grouped = false])
set flockmates n-of (random alonePersons) persons
ask flockmates [
foreach vector [ i ->
set item i vector 0.6
]
]
end
You can use the replace-item reporter to update an item in a list.
Let this [ 1 2 3 4 ]
Let new-list replace-item 3 this “a”
Print new-list
Note that this does not affect the original list: it reports a new list with the specified item replaced with the given value.
Changing an item in a list of lists of similar.. but again, the entire list
Of lists is created anew.
But maybe use links?
In the case of using a list for a turtle to track its relationship with other turtles, or of groups, links are useful, and simplify managing those relationships, and enable things that are very difficult with lists.
LINKS are just another kind of agent, specifically for recording a relationship between two turtles. They have a pair of built-in variables, end1 and end2 that refer to those two turtles. The -own variables of the link can be used to record properties of the relationship. Like “anniversary” or “affinity” or whatever! Links can be directional, so the “intimacy” value can be different depending on the “direction” of the relationship.
Directed-link-breed [ relationships relationship]
Relationships-own [ intimacy ]
to setup-all-relationships
Ask protestors
[ setup-relationship ]
End
To setup-relationship
;; link with everyone else
Create-relationships-to other protestor
[ set intimacy .5 ]
End
The relationship between two turtles can be obtained in several ways.
(Link (this turtle) (that turtle))
Refers to the link from this turtle to that turtle.
Out-Link-neighbors is used to get the set of all turtles linked to from this turtle.
You can also use turtles to represent groups, and links to record membership in that group.
In that case, the members of the group are link-neighbors of the group.
While perhaps not a feature of your model, this opens up the possibility of multiple groups and of agents being members of more than one group, or of tracking things like former members.
Here is a complete, minimal reproducible example of what I think you are looking for. Note that one can just paste it into NetLogo and it compiles and runs. I've made some assumptions here - in particular that intimacy is a protesters-own variable, which it was not in the code you provided, but which your textual description seemed to indicate. Again, using who numbers is not a good idea, but that is a different question and answer. If I have time tomorrow, I might be able to provide you with an example of how one might use agentsets, but if intimacy values can vary from agent-pair to agent-pair, then links is really the way to go.
breed [protesters protester]
globals [numberOfProtesters intimacyVector]
protesters-own [
intimacy
partOfGroup ;initially set to false for all agents
myNRelatedProtesters
]
to setup
clear-all
create-protesters 10
create-intimacyRelationship
reset-ticks
end
to create-intimacyRelationship
ask protesters [
set numberOfProtesters count protesters
set intimacy []
repeat numberOfProtesters [set intimacy lput 0.2 intimacy]
set partOfGroup false
]
end
to updateIntimacy
let nrUngroupedProtesters (count protesters with [partOfGroup = false])
let NRelatedProtesters n-of (random nrUngroupedProtesters) protesters
ask NRelatedProtesters [
foreach ([who] of NRelatedProtesters) [ i -> set intimacy replace-item i intimacy 0.8 ]
set partOfGroup true
]
ask NRelatedProtesters [ show intimacy ]
end
to go
let ProportionProtestersInSubgroup (count protesters with [partOfGroup = true])/ numberOfProtesters
ifelse ((count protesters with [partOfGroup = false])/
numberOfProtesters) > ProportionProtestersInSubgroup
[
updateIntimacy
]
[
stop
]
tick
end
Hope this gets you started.

Concept Behind The Transformed Data Of LDA Model

My question is related to Latent Dirichlet Allocation. Suppose we apply LDA on our dataset, then apply fit transform on that.
the output is a matrix that is a collection of five documents. Each document consists of three topics. othe output is below:
[[ 0.0922935 0.09218227 0.81552423]
[ 0.81396651 0.09409428 0.09193921]
[ 0.05265482 0.05240119 0.89494398]
[ 0.05278187 0.89455775 0.05266038]
[ 0.85209554 0.07338382 0.07452064]]
So, this is the matrix that will be sent to a classification method for an evaluation purpose.
For the classification part, we need the labels for each row. But we do not have the labels which means I have to create them by my own.
One approach could be getting the highest probability for each topic as the corresponding label.
For example, the labels may be like so:
[2,0,2,1,0,]
However, this is very simple example.
I can also consider two highest probability for each document if each documents only has two topics. So, the example would be like this:
[[ 0.0922935 0 0.81552423]
[ 0.81396651 0.09409428 0]
[ 0.05265482 0 0.89494398]
[ 0.05278187 0.89455775 0]
[ 0.85209554 0 0.07452064]]
As you can see I have the rule of keeping the same probability for each label if they have the highest probabilities.
Which approach is correct? Has anyone used any other approach that is more meaningful?
Many thanks in advance!

Return from Prolog predicate

I have a predicate, which is true, if passed such list of pairs, for instance:
translatable([(dog,perro)], [(perro,hund)], [(dog,hund)])
Means - if "dog" translates to "perro", and "perro" translates to "hund", then it is true that "dog" translates to "hund".
Here follows full code. Returns/suggests first member of pair - given ((a, b), a) returns true, given ((a, b), X) returns X = a:
first((First, _), First).
Similar to "first", but for second pair member:
second((_, Second), Second).
This returns true if translatable word exists in list of tuples, and saves translation to Translation: (dog, Translation, [(bed,cama),(dog,perro)]
translation_exists(Word, Translation, [H|T]) :-
first(H, Word), second(H, Translation), !;
translation_exists(Word, Translation, T).
And resulting:
translatable(EnglishSpanish, SpanishGerman, EnglishGerman) :-
forall(member(Pair, EnglishGerman), (
first(Pair, Word),
second(Pair, ResultTranslation),
translation_exists(Word, Translation, EnglishSpanish),
translation_exists(Translation, ResultTranslation, SpanishGerman)
)).
This code returns true/false correctly.
But why, given
translatable([(dog,perro)], [(perro,hund)], X).
It does not returns X = [(dog,hund)]?
EDIT
To be more specific, actual goal is:
to find out if LAST dictionary has translatable pairs (and them only).
Daniel, thanks a lot, I have adopted your suggested member function - great simplification, thank you! This is all the code I have now:
lastIsTranslatable(_, _, []).
lastIsTranslatable(EngSpan, SpanGerm, [(Eng, Germ) | T]) :-
member((Eng, Span), EngSpan),
member((Span, Germ), SpanGerm),
% this is to protect endless [(dog,hund), (dog, hund), ...]
not(member((Eng, Germ), T)),
lastIsTranslatable(EngSpan, SpanGerm, T),
!.
And still, this works great finding True & False:
lastIsTranslatable([(a,b)], [(b,c)], [(a,c)]).
lastIsTranslatable([(a,b)], [(b,c)], [(a,no)]).
But for
lastIsTranslatable([(a,b)], [(b,c)], X).
result is X= [], then, after hitting ";" - false. Why?
Well, running with trace option, I see execution is failing on
not(member((Eng, Germ), T))
But otherwise resulting X will be endlessly filled with (a,c), (a,c)... Maybe there is better way to protect from duplicates?
The reason, basically, is that because EnglishGerman is uninstantiated, member/2 is free to come up with possible lists for it:
?- member((perro,X), List).
member((perro,X), List).
List = [ (perro, X)|_G18493911] ;
List = [_G18493910, (perro, X)|_G18493914] ;
List = [_G18493910, _G18493913, (perro, X)|_G18493917] ;
List = [_G18493910, _G18493913, _G18493916, (perro, X)|_G18493920]
...
This is the most direct issue, but even if you change the flow of data I think you'll still have problems:
translatable1(EnglishSpanish, SpanishGerman, EnglishGerman) :-
member((English,Spanish), EnglishSpanish),
member((Spanish,German), SpanishGerman),
member((English,German), EnglishGerman).
Note that I have foregone your first/2 and second/2 predicates in favor of pattern matching; I think this reads more clearly.
Aside: If you know your list is concrete and you don't want to generate multiple solutions, you can use memberchk/2 to verify that an element exists instead of member/2; it's cheaper and deterministic.
This works better (you get solutions, anyway) but still you get a lot more solutions than you need:
?- translatable1([(dog,perro)], [(perro,hund)], X).
X = [ (dog, hund)|_G18493925] ;
X = [_G18493924, (dog, hund)|_G18493928] ;
X = [_G18493924, _G18493927, (dog, hund)|_G18493931] a
Something which we know that our code does not know is that the cardinality of the result set should be less than or equal to the lowest cardinality of our inputs; if I have fifteen English-Spanish words and twelve Spanish-German words, I can't have more than twelve words in my English-German result. The reason our code doesn't know that is because it is trying to behave like math: our code is basically saying "for every element of English-Spanish, if there exists a matching element of Spanish-German, that is also an element of English-German." This does not tell us how to construct English-German! It only tells us a fact about English-German that we can verify with English-Spanish and Spanish-German! So it's cool, but it isn't quite enough to compute English-German.
Aside: it's conventional in Prolog to use a-b instead of (a,b); it's too easy to lull yourself into believing that Prolog has tuples when it doesn't and the operator precedence can get confusing.
So, how do we tell Prolog how to compute English-German? There are probably lots of ways but I would prefer to use select/3 because our set cardinality constraints (as well as a general sense that it will converge/halt) will emerge naturally from a computation that "uses up" the input sets as it goes.
translatable2([], _, []).
translatable2(_, [], []).
translatable2([Eng-Span|EngSpanRem], SpanGerm, EngGerm) :-
(select(Span-Germ, SpanGerm, SpanGermRem) ->
translatable2(EngSpanRem, SpanGermRem, EngGermRem),
EngGerm = [Eng-Germ|EngGermRem]
;
translatable2(EngSpanRem, SpanGerm, EngGerm)
).
The base cases should be obvious; if we are out of English-Spanish or Spanish-German, there's nothing left to compute. Then the inductive case peels the first item off the English-Spanish list and searches for a Spanish-German translation that matches. If it finds one, it uses it to build the result; otherwise, it just recurs on the remaining English-Spanish list. This way, on each iteration we at least discard an English-Spanish translation from that list, and we discard Spanish-German translations as they are used. So it seems intuitively likely that this will work and terminate without producing a bunch of extra choice points.
It seems to do the trick:
?- translatable2([dog-perro], [perro-hund], X).
X = [dog-hund] ;
X = [dog-hund].
The extra result there is because we hit both terminal cases because both lists became []; this isn't attractive but it isn't anything to worry about really either.
Now one thing that sucks about this solution is that it treats the first two parameters as in-parameters and the last one as an out-parameter and there isn't really anything you can do about this. I don't know if this is an issue for you; translatable/1 should not have this limitation, but because member((Spanish,German), SpanishGerman) happens before member((English,German), EnglishGerman) it winds up generating an infinitely large list, searching in effect for the missing Spanish-German translation.
Still, it feels like it should be possible to come up with a general purpose predicate that works as long as you supply any two of these inputs. I can do that if I know that all three lists are complete and in the same order:
translatable3([], [], []).
translatable3([X-Y|XYs], [Y-Z|YZs], [X-Z|XZs]) :-
translatable3(XYs, YZs, XZs).
And you can see it work like so:
?- translatable3([dog-perro], [perro-hund], X).
X = [dog-hund].
?- translatable3([dog-perro], X, [dog-hund]).
X = [perro-hund].
?- translatable3(X, [perro-hund], [dog-hund]).
X = [dog-perro].
But I don't know enough about your constraints to know if that could be a legitimate answer. My suspicion is no, because languages don't work that way, but who knows?
Anyway, that's three different approaches; I hope one of them is helpful to you!

Make list from list of lists with condition

I'm currently stucked with recursion over lists of lists.
The task is quite simple in any imperative language: iterate over every professor, iterate over professor's course list, and get every course that matches CourseNumber given as predicate argument to output var CourseList.
I have simple solution (other ones just fail with out of stack or returns empty list), but as you can see, it checks only if the head of the course's list matches cond.
get_teaching_courses(CourseNumber, CourseList) :-
findall(Course,
(
professor(_, [Course | _]),
member(CourseNumber, Course)
),
CourseList).
professor fact has next struct:
professor(Name, [ [CourseName , CourseNumber], .... ]).
I am thinking of making predicate over predicate, but I can't achieve it (something wrong with append I guess).
It's been like 2 days I've started learning prolog, and if you can give me any help, advice or link that can help me, I'd appreciate it.
example:
assertz(
professor(
'Bob',
[
['Math', 2],
['PE', 3]
]
)
).
Solution:
get_teaching_courses(CourseNumber, CourseList) :-
findall(CourseName,
(
professor(_, Course),
member([CourseName, CourseNumber], Course)
),
CourseList).
thanks to #CapelliC and his answer.
the problem is the incorrect pattern matching applied in member/2. Try
member([_,CourseNumber], Course)

Prolog Clear List of positive elements without using cuts

I want to clear a list without cutting. I tried:
filter([],[]).
filter([H|T],[H|S]) :-
H<0,
filter(T,S).
filter([H|T],S) :-
H>=0,
filter(T,S).
But it doesn't work.
Here is what happened when I tried:
?- filter([1,0,-6,7,-1],L).
L = [-6,-1]; %false
no
L=[0,-6,-1] %true
Here's one way to do it:
filter([ ],[ ]).
filter([H|T],X) :-
( H > 0 -> X = Y ; X = [H|Y] ),
filter(T,Y).
Because the if-then-else construct in Prolog is sometimes described as having a "hidden cut", meaning that Prolog will not retry (backtrack) the logical outcome in the "if" portion of this construct (it commits to the first and only outcome), it's conceivable that your course instructor might object to this solution (even though no actual cut is used).
But your solution is partially wrong. You lump the zero elements with the positive ones, where your Question's wording suggests only the positive entries need to be "cleared" from the list.