For comparison, my version written in CHR.
:- use_module(library(chr)).
:- chr_constraint child_of/2, candidate_parents/2, grouping/2.
project(N,Ps,Cs) :-
grouping(Ps,Cs),
project_children(N,Ps,Cs),!.
project_children(N,Ps,[C|Cs]) :-
candidate_parents(C,Ps),
project_children(N,Ps,Cs).
project_children(_N,_Ps,[]).
% if a child has two possible sets of mothers, mother must be in the intersection
intersect_parents @ candidate_parents(C,P1) , candidate_parents(C,P2) <=> intersection(P1,P2,P3), candidate_parents(C,P3).
% clean defunct constraints
dups @ child_of(X,Y) \ child_of(X,Y) <=> true.
trash @ grouping([],[]) <=> true.
% grouping with 1 element
single_match @ grouping([P],[C]), candidate_parents(C,_) <=> child_of(C,P).
% single candidate for a childs mother
single_parent @ candidate_parents(C,[P]) <=> child_of(C,P).
% remove child/parent combination from other groupings.
remove_from_groups @ child_of(C,P) \ grouping(Ps,Cs) <=> memberchk(C,Cs), memberchk(P,Ps) | selectchk(C,Cs,NCs), selectchk(P,Ps,NPs), grouping(NPs,NCs).
% Tests.
test1 :-
project(1,[jane,claire],[brian,stephan]),
project(2,[claire,jane],[emma,william]),
project(3,[jane,claire],[william,james]),
project(4,[jane,sophia,claire],[brian,james,isabelle]),
project(4,[claire],[brian]),
project(5,[jane],[emma]).
test2 :-
project(1,[jane,claire],[brian,stephan]),
project(2,[claire,jane],[emma,william]),
project(3,[jane,claire],[william,james]),
project(4,[jane,sophia,claire],[brian,james,isabelle]),
project(4,[claire],[brian]),
project(5,[jane],[emma]),
project(7,[sally,sandy],[grace,miriam]).
test3 :-
project(1,[jane,claire],[brian, stephan]),
project(2,[jane,emma],[stephan, jones]).
P.S. My code is probably badly formatted also 