Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
1 result

Target

Select target project
  • guyslain.naves/dissiml
1 result
Select Git revision
  • master
1 result
Show changes
Commits on Source (4)
module Make = functor (K : Map.OrderedType) ->
struct
module M = Map.Make(K)
let add_to_list key value map =
M.update key
(function
| None -> Some [value]
| Some list -> Some (value::list)
)
map
let insert ~get_key map value =
add_to_list (get_key value) value map
let sort ~get_key values =
values
|> List.fold_left (insert ~get_key) M.empty
|> M.bindings
|> List.map snd
end
module Make :
functor (K : Map.OrderedType) ->
sig
val sort : get_key:('a -> K.t) -> 'a list -> 'a list list
end
(** This modules contains an algorithm that, given a PQ-tree T on X,
and a chain S_0 = {s}, S_1, ... S_k = X, builds a PQ-tree
representing all the permutations represented by T and for which
each S_i is an interval, if such a PQ-tree exists.
This could be done by the Booth-Lueker interval insertion
algorithm (functionalBL or imperativeBL), with k insertions. This
algorithm improves upon repeated insertion by having a linear-time
complexity.
*)
open DataStruct
let consecutives list =
let rec go accu = function
| [] | [_] -> List.rev accu
| x::((y::_) as tail) -> go ((x,y)::accu) tail
in
go [] list
let last = function
| [] -> invalid_arg "last on empty list"
| h::t -> List.fold_left (fun _ e -> e) h t
module IntPair =
struct
type t = (int * int)
let compare (x1,y1) (x2,y2) =
let diff = x1 - x2 in
if diff = 0 then y1 - y2
else diff
end
module IntPairSorter = BucketSort.Make(IntPair)
module IntPairMap = Map.Make(IntPair)
(* We parametrize the whole algorithm by the chain, encoded by a rank function d,
d(x) is such that x \in S_{d(x)} \setminus S_{d(x)-1}, and d(s) = 0.
*)
module Make =
functor (Chain : sig type t val s : t val d : t -> int end) ->
struct
type elt = Chain.t
type pqtree = elt PqTree.t
open Chain
open PqTree
let q = function
| [_1;_2] as children -> P children
| children -> Q children
type external_subtree =
| ConstantSubtree of int * pqtree
| IncreasingSubtree of int * pqtree list * int (* an increasing subtree is necessarily a Q-node *)
let bounds = function
| ConstantSubtree (delta,_) -> (delta, delta)
| IncreasingSubtree (delta_min, _, delta_max) -> (delta_min, delta_max)
let d_min = function
| ConstantSubtree (delta,_) -> delta
| IncreasingSubtree (delta,_,_) -> delta
let d_max = function
| ConstantSubtree (delta, _) -> delta
| IncreasingSubtree (_,_,delta) -> delta
let amplitude subtrees = (d_min (List.hd subtrees), d_max (last subtrees))
let tree_of_external_tree = function
| ConstantSubtree (_,t) -> t
| IncreasingSubtree (_, children,_) -> q children
let is_increasing_sequence subtrees =
List.for_all
(fun (subtree1, subtree2) -> d_max subtree1 <= d_min subtree2)
(consecutives subtrees)
let force_increasing_sequence subtrees =
if is_increasing_sequence subtrees then Some subtrees
else let reversed = List.rev subtrees in
if is_increasing_sequence reversed then Some reversed
else None
let expand_increasing_sequence subtrees =
let contract = function
| ConstantSubtree (_,t) -> [t]
| IncreasingSubtree (_, children, _) -> children
in
List.concat_map contract subtrees
let sort_into_increasing_sequence subtrees =
IntPairSorter.sort ~get_key:bounds subtrees
type sorting_group =
| Group of int * pqtree list
| Single of external_subtree
let group_d_min = function
| Group (delta,_) -> delta
| Single subtree -> d_min subtree
let group_d_max = function
| Group (delta,_) -> delta
| Single subtree -> d_max subtree
let sort_children_of_p_node children =
let group = function
| (ConstantSubtree (delta,_)::_) as trees -> [ Group (delta, List.map tree_of_external_tree trees) ]
| trees -> List.map (fun t -> Single t) trees
in
children
|> sort_into_increasing_sequence
|> List.concat_map group
let is_increasing_group_sequence groups =
List.for_all
(fun (group1, group2) -> group_d_max group1 <= group_d_min group2)
(consecutives groups)
let groups_amplitude groups =
( group_d_min (List.hd groups), group_d_max (last groups))
type centered_subtree =
| Balanced of int * pqtree
| Skew of int * pqtree list * int
let tree_of_centered_tree = function
| Balanced (_, tree) -> tree
| Skew (_,children,_) -> q children
let bounds_centered = function
| Balanced (delta,_) -> (delta, delta)
| Skew (delta0,_,delta1) -> (delta0,delta1)
let contract_centered = function
| Balanced (_,single) -> [ single ]
| Skew (_,children,_) -> children
type result =
| NotRepresentable
| CenteredSubtree of centered_subtree
| ExternalSubtree of external_subtree
type classification =
| Unclassifiable
| Internal of external_subtree list * centered_subtree * external_subtree list
| External of external_subtree list
let classify children =
let rec go classification children =
match classification, children with
| _, [] -> classification
| Unclassifiable, _
| _, NotRepresentable::_ -> Unclassifiable
| Internal (left, at_s, right), ExternalSubtree child::children ->
go (Internal (left, at_s, child::right)) children
| External left_of_s, ExternalSubtree child::children ->
go (External (child::left_of_s)) children
| External left_of_s, CenteredSubtree centered::children ->
go (Internal (left_of_s, centered, [])) children
| Internal _, CenteredSubtree _ :: _ -> Unclassifiable
in
go (External []) children
type queue =
{ left_bound : int;
left_trees : pqtree list;
right_trees : pqtree list;
right_bound : int
}
let init_queue = function
| Balanced (bound, tree) ->
{ left_bound = bound; left_trees = []; right_trees = [tree]; right_bound = bound }
| Skew (left_bound, right_trees, right_bound) ->
{ left_bound; left_trees = []; right_trees; right_bound }
let is_balanced queue = queue.left_bound = queue.right_bound
let compact queue =
match List.rev_append queue.left_trees queue.right_trees with
| [single_child] when is_balanced queue -> Balanced (queue.left_bound, single_child)
| children when is_balanced queue -> Balanced (queue.left_bound, q children)
| children -> Skew (queue.left_bound, children, queue.right_bound)
let enqueue_right group queue =
let (right_trees, right_bound) = match group with
| Group (bound, children) -> (P children :: queue.right_trees, bound)
| Single (ConstantSubtree (bound, subtree)) -> (subtree :: queue.right_trees, bound)
| Single (IncreasingSubtree (_, subtrees, bound)) -> (List.rev_append subtrees queue.right_trees, bound)
in
{ queue with right_trees; right_bound }
let enqueue_left group queue =
let (left_trees, left_bound) = match group with
| Group (bound, children) -> (P children :: queue.left_trees, bound)
| Single (ConstantSubtree (bound, subtree)) -> (subtree :: queue.left_trees, bound)
| Single (IncreasingSubtree (_,subtrees, bound)) -> (List.rev_append subtrees queue.left_trees, bound)
in
{ queue with left_trees; left_bound }
let rec enqueue groups queue =
let max_bound = max queue.left_bound queue.right_bound in
match groups with
| [] ->
CenteredSubtree (compact queue)
| Group (dist,children) :: groups when dist >= max_bound ->
queue
|> compact
|> tree_of_centered_tree
|> (fun tree -> Balanced (dist, P (tree :: children)))
|> init_queue
|> enqueue groups
| single :: groups when group_d_min single >= max_bound ->
queue
|> compact
|> init_queue
|> enqueue_right single
|> enqueue groups
| any :: groups when group_d_min any >= queue.right_bound ->
queue
|> enqueue_right any
|> enqueue groups
| any :: groups when group_d_min any >= queue.left_bound ->
queue
|> enqueue_left any
|> enqueue groups
| _ (* when group_d_min any < min queue.left_bound queue.right_bound *) ->
NotRepresentable
let build_p_internal left_subtrees s_subtree right_subtrees =
let increasing_subtrees =
sort_children_of_p_node (List.rev_append left_subtrees right_subtrees)
in
enqueue increasing_subtrees (init_queue s_subtree)
let build_q_internal left_subtrees s_subtree right_subtrees =
if not (is_increasing_sequence left_subtrees && is_increasing_sequence right_subtrees) then
NotRepresentable
else
let (min_center, max_center) = bounds_centered s_subtree in
let (min_left, max_left) = amplitude left_subtrees in
let (min_right, max_right) = amplitude right_subtrees in
let lefts = expand_increasing_sequence left_subtrees in
let rights = expand_increasing_sequence right_subtrees in
let centered_subtree left_bound trees right_bound =
if left_bound = right_bound then
CenteredSubtree (Balanced (left_bound, q trees))
else
CenteredSubtree (Skew (left_bound, trees, right_bound))
in
if max(min_center, max_center) <= min (min_left, min_right) then
centered_subtree
max_left
(List.rev lefts @ [tree_of_centered_tree s_subtree] @ rights)
max_right
else if min_left >= min_center && max_center <= min_right then
centered_subtree
max_left
(List.rev lefts @ contract_centered s_subtree @ rights)
max_right
else if min_right >= min_center && max_center <= min_left then
centered_subtree
max_right
(List.rev rights @ contract_centered s_subtree @ lefts)
max_left
else NotRepresentable
let contract_group = function
| Group (_, subtrees) -> [ P subtrees ]
| Single (ConstantSubtree (_,subtree)) -> [ subtree ]
| Single (IncreasingSubtree (_,subtrees,_)) -> subtrees
let build_p_external unsorted_subtrees =
let groups = sort_children_of_p_node unsorted_subtrees in
match groups with
| _ when not (is_increasing_group_sequence groups) -> NotRepresentable
| [ Group (delta,children) ] ->
ExternalSubtree (ConstantSubtree (delta, P children))
| _ ->
let (delta_min, delta_max) = groups_amplitude groups in
let subtrees = List.concat_map contract_group groups in
ExternalSubtree (IncreasingSubtree (delta_min, subtrees, delta_max))
let build_q_external subtrees =
match force_increasing_sequence subtrees with
| None -> NotRepresentable
| Some subtrees ->
ExternalSubtree (
let (delta_min, delta_max) = amplitude subtrees in
if delta_min = delta_max then
ConstantSubtree (delta_min, Q (List.map tree_of_external_tree subtrees))
else
IncreasingSubtree (delta_min, expand_increasing_sequence subtrees, delta_max)
)
let dispatch root children =
match root, classify children with
| Leaf x, _ when x = s ->
CenteredSubtree (Balanced (0, Leaf s))
| Leaf x, _ ->
ExternalSubtree (ConstantSubtree (d x, Leaf x))
| _, Unclassifiable -> NotRepresentable
| P _, Internal (left_of_s, at_s, right_of_s) ->
build_p_internal left_of_s at_s (List.rev right_of_s)
| P _, External subtrees ->
build_p_external subtrees
| Q _, Internal (left_of_s, at_s, right_of_s) ->
build_q_internal left_of_s at_s (List.rev right_of_s)
| Q _, External subtrees ->
build_q_external subtrees
let children = function
| Leaf _ -> []
| P children
| Q children -> children
let rec solve tree =
children tree
|> List.map solve
|> dispatch tree
let insert_chain tree =
match solve tree with
| NotRepresentable -> None
| CenteredSubtree (Balanced (_,tree)) -> Some tree
| CenteredSubtree (Skew (_,trees,_)) -> Some (q trees)
| ExternalSubtree (ConstantSubtree (_, tree)) -> Some tree
| ExternalSubtree (IncreasingSubtree (_, trees, _)) -> Some (q trees)
end
module Make :
functor (Chain : sig type t val s : t val d : t -> int end) ->
sig
val insert_chain : Chain.t PqTree.t -> Chain.t PqTree.t option
end
(** This module defines what is a PQ-tree over some set, as an
inductive data type. A PQ-tree is a data structure encoding a
subset of permutations. Not every subset of permutations can be
encoded as a PQ-tree. It will be convenient to interpret
permutations as ordered sequences of elements.
Pq-trees where introduced in: Kellogg S. Booth, George S. Lueker,
{i Testing for the consecutive ones property, interval graphs, and
graph planarity using PQ-tree algorithms}, Journal of Computer and
System Sciences, Volume 13, Issue 3, 1976, Pages 335-379, ISSN
0022-0000, {{:https://doi.org/10.1016/S0022-0000(76)80045-1}}.
This module also defines some operations on PQ-trees, in particular on
permutations encoded by a PQ-tree, and other convenience
functions. *)
(** The type of PQ-tree, over a set of elements of type 'elt.
A leaf [Leaf a] encodes the permutation {%$(a)$%} on the singleton set {%$\{a\}$%}.
A P-node [P [t1;...;tn]] encodes all the permutations that can be
obtained by concatenating {%$\sigma_1,\ldots,\sigma_n$%} in an
arbitrary order, where each {%$\sigma_i$%} is a permutation
encoded by [ti].
A Q-node [Q [t1,...tn]] encodes all the permutations {%$\sigma_1
\sigma_2 \ldots \sigma_n$%} and
{%$\sigma_n,\ldots,\sigma_2,\sigma_1$%} where each {%$\sigma_i$%}
is a permutation encoded by [ti].
*)
type 'elt t =
| Leaf of 'elt
| P of 'elt t list
| Q of 'elt t list
(** [length tree] is the number of elements in the PQ-tree [tree], that is
the length of permutations encoded by the PQ-tree.
@param tree a PQ-tree
@return the number of elements in the permutations encoded by [tree]
*)
val length : 'elt t -> int
(** An enumeration of the permutations encoded by a given PQ-tree.
@param tree a PQ-tree
@return the sequence of all the permutations encoded by [tree]
*)
val enumerate_permutation : 'elt t -> 'elt list Seq.t
(** The number of permutations encoded by a given PQ-tree.
@param tree a PQ-tree
return the number of permutations encoded by [tree]
*)
val count_permutations : 'elt t -> Big_int_Z.big_int
(** A random permutation among those encoded by a given PQ-tree.
@param tree a PQ-tree
@return a random permutation, as a list of elements, chosen uniformly randomly among all permutations encoded by [tree]
*)
val sample_permutation : 'elt t -> 'elt list
(** A random interval for the set of permutations encoded by a given
PQ-tree. An {i interval} for a set of permutation
{%$\mathcal{P}$%} is a subset of elements that appears
consecutively in any permutation in the set {%$\mathcal{P}$%}. The
interval is sampled by first sampling a uniformly chosen
permutation, then sampling in this permutation a random
subsequence of elemnts. This sampling is not uniform in general.
@param tree a PQ-tree
@return a random interval for the set of permutations encoded by [tree]
*)
val sample_interval : 'elt t -> 'elt list
(** The set of elements in a PQ-tree, in left-to-right order. In
particular, the frontier is one of the encoded permutation of the
PQ-tree.
@param tree a PQ-tree
@return the frontier of [tree]: its elements in left-to-right order.
*)
val frontier : 'elt t -> 'elt list
(** A shrinking function that can be used for property testing: given
a PQ-tree [tree], it returns a sequence of PQ-trees obtained each
by contracting one node of [tree].
@param tree a PQ-tree
@return a sequence of smaller PQ-trees.
*)
val shrink : 'elt t -> 'elt t Seq.t
(** A PQ-tree that encodes the same set of permutations, obtained by
reordering the children of each P-node in a uniformly random
order, and reversing the order of children under Q-nodes with
probability 1/2.
@param tree a PQ-tree
@return a random PQ-tree encoding the same set of permutations
*)
val shuffle : 'elt t -> 'elt t
(** Checks whether a PQ-tree is well-formed, that is each P-node has
at least two children (otherwise it can be contracted), and each
Q-node has a least three children (otherwise it can be replaced by
a P-node or contracted).
@param tree a PQ-tree
@return [true] if [tree] is well-formed.
*)
val is_well_formed : 'elt t -> bool
(** A skeleton of a PQ-tree defines the structure of the PQ-tree in
terms of P-node and Q-node, by there is no specified elements.
Each element is replaced by a {i hole}. This is equivalent to
quotienting the set of PQ-trees by the ground set on which the
permutations are defined (thus abstracting the ground set).
*)
module Skeleton :
sig
(** The type of a PQ-tree skeleton, made of leafs ([Hole]), P-nodes and Q-nodes. *)
type skeleton =
| Hole
| HoledP of skeleton list
| HoledQ of skeleton list
(** Derive a PQ-tree on an interval {%$\{0,1,\ldots,n-1\}$%}, from a
skeleton, by replacing each node by a distinct integer, in
increasing order from left to right.
@param skeleton a skeleton of PQ-tree
@return a PQ-tree whose skeleton is [skeleton] and whose
frontier is {%$\{0,1,\ldots,n-1\}$%}
*)
val to_pqtree : skeleton -> int t
end
(** A type for modules providing enumeration capabilities, as defined
by the Feat library, enriched with the capability to enumerate
over PQ-trees over a given type of elements.
*)
module type EnumerateSig =
sig
(** The type of elements of the encoded permutations. *)
type elt
include FeatCore.EnumSig.ENUM
(** An enumeration of all PQ-trees over some type of elements *)
val enumeration : elt t enum
end
module MakeEnumerate : functor (RND : FeatCore.RandomSig.S) -> EnumerateSig with type elt = int
(** A functor to build a function to enumerate over PQ-trees. *)
module MakeEnumerate :
functor (RND : FeatCore.RandomSig.S) -> EnumerateSig with type elt = int
(** A pre-application of the [MakeEnumerate] functor, to enumerate of
PQ-trees over integers.
*)
module Enum : EnumerateSig with type elt = int
val sample : int -> int t
(** Sample a uniformly random PQ-tree over an interval
{%$\{0,1,\ldots,n-1\}$%} of integers.
@param n the length of permutations encoded by the sampled PQ-tree
return the sampled PQ-tree over permutations of length [n]
*)
val sample : int -> int t
(** A type for modules providing functions to define a canonical
representation of equivalent PQ-trees, where two PQ-trees are
equivalent when they encode the same set of permutations.
*)
module type CanonicalSig =
sig
(** The type of elements of the permutations encoded by the PQ-trees *)
type elt
(** Finds the canonical representation of a subset of permutations
encoded by a PQ-tree.
@param [tree] a PQ-tree
@return A canonical PQ-tree representing the set of
permutation encoded by [tree]
*)
val canonical : elt t -> elt t
(** Compares the set of permutations encoded by two PQ-trees. This
is done by comparing the two canonical PQ-tree representations for
each set of permutations.
@param tree1 A PQ-tree
@param tree2 A PQ-tree
@return the result of comparing the two subsets of
permutations encoded by [tree1] and [tree2]
*)
val compare : elt t -> elt t -> int
(** Compares two PQ-trees. Two different PQ-trees encoding the
same set of permutations will be considered different under
this comparison.
@param tree1 A PQ-tree
@param tree2 A PQ-tree
@return the result of comparing [tree1] and [tree2]
*)
val compare' : elt t -> elt t -> int
(** The type of PQ-trees with elements of type [elt]. This allows
a module with this type to be used in a functor expecting an
ordered type. *)
type nonrec t = elt t
end
(** A functor to make comparison and canonization function for
PQ-trees, given an ordered type of elements.
*)
module MakeCanonical : functor (C : Map.OrderedType) -> CanonicalSig with type elt = C.t
(** The type of modules proposing minimal [Map] functionalities. *)
module type EltMap =
sig
type key
......@@ -72,24 +248,65 @@ sig
end
(** The type of modules allowing to check whether a permutation is
contained in the set of permutations represented by a PQ-tree. *)
module type CheckPermutationSig =
sig
(** The type of elements on which the permutations are defined. *)
type elt
(** Checks whether a permutation is contained in the subset of
permutations encoded by a PQ-tree.
@param tree a PQ-tree
@param permutation a permutation
@return [true] if [permutation] is contained in the set of
permutations represented by [tree].
*)
val contains_permutation : elt t -> elt list -> bool
(** A checker for a PQ-tree is able to check whether any
permutation is contained in the set of permuttaions
represented by the PQ-tree. This allows to build a checker
once and use it multiple times for many permutations. *)
type checker
(** Computes a checker for a given PQ-tree.
@param tree a PQ-tree
@return a checker for [tree]
*)
val checker : elt t -> checker
(** Decides whether a permutation is contained in the set of
permutations represented by a PQ-tree
@param checker a checker for a PQ-tree [tree]
@param permutation a permutation
@return [true] if [permutation] is contained in the set of
permutations represented by [tree]
*)
val checks : checker -> elt list -> bool
val contains_permutation : elt t -> elt list -> bool
end
module MakeCheckPermutation : functor(M : EltMap) -> CheckPermutationSig with type elt = M.key
(** A functor to derive functions to check whether a permutation is
contained in the set of permutations represented by a PQ-tree. *)
module MakeCheckPermutation :
functor(M : EltMap) -> CheckPermutationSig with type elt = M.key
(** The type of modules encoding a formattable type. *)
module type PrintableType = sig
type t
val print : Format.formatter -> t -> unit
end
module MakePrinter : functor (Prt : PrintableType) -> PrintableType with type t = Prt.t t
(** A functor to derive a function to format a PQ-tree. *)
module MakePrinter :
functor (Prt : PrintableType) -> PrintableType with type t = Prt.t t
This project contains implementations in Ocaml of algorithms for
recognition of Robinson dissimilarities, as well as random generators
of dissimilarities. The primary goal is to provide an implementation
of the algorithm proposed by Carmona, Chepoi, Naves and Préa. We
actually give two implementations, in imperativeCCNP.ml (a mostly
imperative implementation) and robinsonCCNP.ml (a more functional
implementation). The two implementations are equivalent, except for
their respective performance which slightly differs from one another.
0) Need OCaml installed. Easy way:
sudo apt-get install opam
opam init
......@@ -11,8 +25,7 @@ see https://ocaml.org/learn/tutorials/up_and_running.html
dune build
2) execute:
cat my_matrix | dune exec copoint
cat instance_1 | sed "s/,\|\[\|]//g" | dune exec copoint
cat matrices/mat1 | dune exec copoint
Run test:
......