]> Git — Sourcephile - gargantext.git/blob - src/Gargantext/Viz/Phylo/Example.hs
Some refactoring
[gargantext.git] / src / Gargantext / Viz / Phylo / Example.hs
1 {-|
2 Module : Gargantext.Viz.Phylo.Example
3 Description : Phylomemy example based on history of Cleopatre.
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
8 Portability : POSIX
9
10 -- | Cesar et Cleôpatre
11 -- Exemple de phylomemie
12 -- French without accents
13
14
15 TODO:
16 - split the functions : RAW -> Document -> Ngrams
17
18 -- reverse history: antechronologique
19 -- metrics support
20
21
22 -}
23
24 {-# LANGUAGE NoImplicitPrelude #-}
25 {-# LANGUAGE FlexibleContexts #-}
26 {-# LANGUAGE OverloadedStrings #-}
27
28 module Gargantext.Viz.Phylo.Example where
29
30 import Control.Lens hiding (both, Level)
31 import Data.List (concat, union, intersect, tails, tail, head, last, null, zip, sort, length, any, (++), nub)
32 import qualified Data.List as List
33 import Data.Text (Text, unwords, toLower, words)
34 import Data.Tuple.Extra
35 import Data.Semigroup (Semigroup)
36
37 import Data.Map (Map, elems, member, adjust, singleton, (!), keys, restrictKeys, mapWithKey)
38 import qualified Data.Map as Map
39
40 import Data.Vector (Vector, fromList, elemIndex)
41 import qualified Data.Vector as Vector
42 import qualified Data.Maybe as Maybe
43
44 import Data.Tuple (fst, snd)
45 import qualified Data.Tuple as Tuple
46
47 import Data.Bool (Bool, not)
48 import qualified Data.Bool as Bool
49
50 import Data.Set (Set)
51 import qualified Data.Set as Set
52 import qualified Data.Matrix as DM'
53
54 import Gargantext.Text.Metrics.FrequentItemSet (fisWithSizePolyMap, Size(..))
55 import Gargantext.Text.Terms.Mono (monoTexts)
56 import Gargantext.Prelude hiding (head)
57 import Gargantext.Viz.Phylo
58 import Gargantext.Viz.Phylo.Tools
59
60 ------------------------------------------------------------------------
61 -- | Types | --
62
63
64 -- | Document : a piece of Text linked to a Date
65 data Document = Document
66 { date :: Date
67 , text :: Text
68 } deriving (Show)
69 -- | Corpus : a list of Documents
70 type Corpus = [Document]
71
72
73 type MapList = [Ngrams]
74 type PeriodeSize = Int
75 -- data Periodes b a = Map (b,b) a
76 type Occurrences = Int
77
78 data Level = Level_m1 | Level_0 | Level_1 | Level_2 | Level_N
79 deriving (Show, Eq, Enum, Bounded)
80
81 data LinkLvlLabel = Link_m1_0 | Link_0_m1 | Link_1_0 | Link_0_1 | Link_x_y
82 deriving (Show, Eq, Enum, Bounded)
83
84 data LinkLvl = LinkLvl
85 { linkLvlLabel :: LinkLvlLabel
86 , linkLvlFrom :: Int
87 , linkLvlTo :: Int
88 } deriving (Show)
89
90
91 data PhyloError = LevelDoesNotExist
92 | LevelUnassigned
93 deriving (Show)
94
95
96 --------------------------------------------------------------------
97 phyloExampleFinal :: Phylo
98 phyloExampleFinal = undefined
99
100 --------------------------------------------------------------------
101 appariement :: Map (Date, Date) (Map (Set Ngrams) Int)
102 appariement = undefined
103
104 ------------------------------------------------------------------------
105 -- | STEP 10 | -- Incrementaly cluster the PhyloGroups n times, link them through the Periods and build level n of the Phylo
106
107
108 ------------------------------------------------------------------------
109 -- | STEP 8 | -- Cluster the Fis
110
111 ------------------------------------------------------------------------
112 -- | STEP 9 | -- Link the PhyloGroups of level 1 through the Periods
113
114 shouldPair :: PhyloGroup -> PhyloGroup -> Bool
115 shouldPair g g' = (not . null) $ intersect (getNgrams g) (getNgrams g')
116
117
118 getKeyPair :: (Int,Int) -> Map (Int,Int) a -> (Int,Int)
119 getKeyPair (x,y) m = case findPair (x,y) m of
120 Nothing -> panic "[ERR][Viz.Phylo.Example.getKeyPair] Nothing"
121 Just i -> i
122 where
123 --------------------------------------
124 findPair :: (Int,Int) -> Map (Int,Int) a -> Maybe (Int,Int)
125 findPair (x,y) m
126 | member (x,y) m = Just (x,y)
127 | member (y,x) m = Just (y,x)
128 | otherwise = Nothing
129 --------------------------------------
130
131 -- |
132 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
133 listToCombi f l = [ (f x, f y) | (x:rest) <- tails l
134 , y <- rest
135 ]
136
137 fisToCooc :: Map (Date, Date) Fis -> Phylo -> Map (Int, Int) Double
138 fisToCooc m p = map (/docs)
139 $ foldl (\mem x -> adjust (+1) (getKeyPair x mem) mem) cooc
140 $ concat
141 $ map (\x -> listToCombi (\x -> ngramsToIdx x p) $ (Set.toList . fst) x) fis
142 where
143 --------------------------------------
144 fis :: [(Clique,Support)]
145 fis = concat $ map (\x -> Map.toList x) (elems m)
146 --------------------------------------
147 fisNgrams :: [Ngrams]
148 fisNgrams = foldl (\mem x -> union mem $ (Set.toList . fst) x) [] fis
149 --------------------------------------
150 docs :: Double
151 docs = fromIntegral $ foldl (\mem x -> mem + (snd x)) 0 fis
152 --------------------------------------
153 cooc :: Map (Int, Int) (Double)
154 cooc = Map.fromList $ map (\x -> (x,0)) (listToCombi (\x -> ngramsToIdx x p) fisNgrams)
155 --------------------------------------
156
157
158
159 phyloWithAppariement1 :: Phylo
160 phyloWithAppariement1 = phyloLinked_0_1
161
162 ------------------------------------------------------------------------
163 -- | STEP 8 | -- Find the Fis out of Documents and Ngrams and build level 1 of the Phylo
164
165 phyloLinked_0_1 :: Phylo
166 phyloLinked_0_1 = phyloToLinks lvl_0_1 phyloLinked_1_0
167
168 lvl_0_1 :: LinkLvl
169 lvl_0_1 = (LinkLvl Link_0_1 0 1)
170
171 phyloLinked_1_0 :: Phylo
172 phyloLinked_1_0 = phyloToLinks lvl_1_0 phyloWithGroups1
173
174 lvl_1_0 :: LinkLvl
175 lvl_1_0 = (LinkLvl Link_1_0 1 0)
176
177 phyloWithGroups1 :: Phylo
178 phyloWithGroups1 = updatePhyloByLevel Level_1 phyloLinked_m1_0
179
180 cliqueToGroup :: PhyloPeriodId -> Int -> Int -> Ngrams -> (Clique,Support) -> Phylo -> PhyloGroup
181 cliqueToGroup period lvl idx label fis p = PhyloGroup ((period, lvl), idx)
182 label
183 (sort $ map (\x -> ngramsToIdx x p)
184 $ Set.toList
185 $ fst fis
186 )
187 (singleton "support" (fromIntegral $ snd fis))
188 [] [] [] []
189
190 fisToPhyloLevel :: Map (Date, Date) Fis -> Phylo -> Phylo
191 fisToPhyloLevel m p = over (phylo_periods . traverse)
192 (\period ->
193 let periodId = _phylo_periodId period
194 fisList = zip [1..] (Map.toList (m ! periodId))
195 in over (phylo_periodLevels)
196 (\levels ->
197 let groups = map (\fis -> cliqueToGroup periodId 1 (fst fis) "" (snd fis) p) fisList
198 in (PhyloLevel (periodId, 1) groups) : levels
199 ) period
200 ) p
201
202 -- | To preserve nonempty periods from filtering, please use : filterFisBySupport False ...
203 phyloFisFiltered :: Map (Date, Date) Fis
204 phyloFisFiltered = filterFisBySupport True 1 (filterFisByNested phyloFis)
205
206 filterFisBySupport :: Bool -> Int -> Map (Date, Date) Fis -> Map (Date, Date) Fis
207 filterFisBySupport empty min m = case empty of
208 True -> Map.map (\fis -> filterMinorFis min fis) m
209 False -> Map.map (\fis -> filterMinorFisNonEmpty min fis) m
210
211 filterMinorFis :: Int -> Fis -> Fis
212 filterMinorFis min fis = Map.filter (\s -> s > min) fis
213
214 filterMinorFisNonEmpty :: Int -> Fis -> Fis
215 filterMinorFisNonEmpty min fis = if (Map.null fis') && (not $ Map.null fis)
216 then filterMinorFisNonEmpty (min - 1) fis
217 else fis'
218 where
219 fis' = filterMinorFis min fis
220
221 doesContains :: [Ngrams] -> [Ngrams] -> Bool
222 doesContains l l'
223 | null l' = True
224 | length l' > length l = False
225 | elem (head l') l = doesContains l (tail l')
226 | otherwise = False
227
228 doesAnyContains :: Set Ngrams -> [Set Ngrams] -> [Set Ngrams] -> Bool
229 doesAnyContains h l l' = any (\c -> doesContains (Set.toList c) (Set.toList h)) (l' ++ l)
230
231 filterNestedCliques :: Set Ngrams -> [Set Ngrams] -> [Set Ngrams] -> [Set Ngrams]
232 filterNestedCliques h l l'
233 | null l = if doesAnyContains h l l'
234 then l'
235 else h : l'
236 | doesAnyContains h l l' = filterNestedCliques (head l) (tail l) l'
237 | otherwise = filterNestedCliques (head l) (tail l) (h : l')
238
239
240 filterFisByNested :: Map (Date, Date) Fis -> Map (Date, Date) Fis
241 filterFisByNested = map (\fis -> restrictKeys fis
242 $ Set.fromList
243 $ filterNestedCliques (head (keys fis)) (keys fis) []
244 )
245
246 phyloFis :: Map (Date, Date) Fis
247 phyloFis = termsToFis phyloTerms
248
249 termsToFis :: Map (Date, Date) [Document]
250 -> Map (Date, Date) Fis
251 termsToFis = corpusToFis (words . text)
252
253 -- | TODO: parameters has to be checked
254 -- | TODO FIS on monotexts
255 corpusToFis :: (Document -> [Ngrams])
256 -> Map (Date, Date) [Document]
257 -> Map (Date, Date) (Map (Set Ngrams) Int)
258 corpusToFis f = map (\d -> fisWithSizePolyMap (Segment 1 20) 1 (map f d))
259
260
261 ------------------------------------------------------------------------
262 -- | STEP 7 | -- Link level -1 to level 0
263
264
265 phyloLinked_m1_0 :: Phylo
266 phyloLinked_m1_0 = phyloToLinks lvl_m1_0 phyloLinked_0_m1
267
268 lvl_m1_0 :: LinkLvl
269 lvl_m1_0 = (LinkLvl Link_m1_0 (-1) 0)
270
271
272 ------------------------------------------------------------------------
273 -- | STEP 6 | -- Link level 0 to level -1
274
275
276 addPointer :: Semigroup field
277 => ASetter current target identity (field -> field)
278 -> field -> current -> target
279 addPointer field targetPointer current =
280 set field (<> targetPointer) current
281
282 containsIdx :: [Int] -> [Int] -> Bool
283 containsIdx l l'
284 | null l' = False
285 | last l < head l' = False
286 | head l' `elem` l = True
287 | otherwise = containsIdx l (tail l')
288
289 shouldLink :: LinkLvl -> [Int] -> [Int] -> Bool
290 shouldLink lvl current target = case linkLvlLabel lvl of
291 Link_0_m1 -> containsIdx target current
292 Link_m1_0 -> containsIdx target current
293 Link_0_1 -> containsIdx target current
294 Link_1_0 -> containsIdx target current
295 Link_x_y -> undefined
296 _ -> panic ("error link level to be defined")
297
298 linkGroupToGroups :: LinkLvl -> PhyloGroup -> [PhyloGroup] -> PhyloGroup
299 linkGroupToGroups lvl current targets
300 | linkLvlFrom lvl < linkLvlTo lvl = setLevelParents current
301 | linkLvlFrom lvl > linkLvlTo lvl = setLevelChilds current
302 | otherwise = current
303 where
304 setLevelChilds :: PhyloGroup -> PhyloGroup
305 setLevelChilds = over (phylo_groupLevelChilds) addPointers
306
307 setLevelParents :: PhyloGroup -> PhyloGroup
308 setLevelParents = over (phylo_groupLevelParents) addPointers
309
310 addPointers :: [Pointer] -> [Pointer]
311 addPointers lp = lp ++ Maybe.mapMaybe (\target -> if shouldLink lvl (_phylo_groupNgrams current)
312 (_phylo_groupNgrams target )
313 then Just ((getGroupId target),1)
314 else Nothing
315 ) targets
316
317 addPointers' :: [Pointer] -> [Pointer]
318 addPointers' lp = lp ++ map (\target -> ((getGroupId target),1)) targets
319
320 linkGroupsByLevel :: LinkLvl -> Phylo -> [PhyloGroup] -> [PhyloGroup]
321 linkGroupsByLevel lvl p groups = map (\group ->
322 if getGroupLevel group == linkLvlFrom lvl
323 then linkGroupToGroups lvl group (getGroupsWithFilters (linkLvlTo lvl) (getGroupPeriod group) p)
324 else group ) groups
325
326 phyloToLinks :: LinkLvl -> Phylo -> Phylo
327 phyloToLinks lvl p = over ( phylo_periods
328 . traverse
329 . phylo_periodLevels
330 . traverse
331 . phylo_levelGroups
332 )
333 (linkGroupsByLevel lvl p) p
334
335 phyloLinked_0_m1 :: Phylo
336 phyloLinked_0_m1 = phyloToLinks lvl_0_m1 phyloWithGroups0
337
338 lvl_0_m1 :: LinkLvl
339 lvl_0_m1 = (LinkLvl Link_0_m1 0 (-1))
340
341
342 ------------------------------------------------------------------------
343 -- | STEP 5 | -- Build level 0 (for the moment it's just a durty copy of level -1)
344
345
346 setGroupIdLvl :: Int -> PhyloGroup -> PhyloGroup
347 setGroupIdLvl lvl (PhyloGroup ((period, lvl'), idx) gLabel gNgrams gQ gPP gPC gLP gLC)
348 = PhyloGroup ((period, lvl), idx) gLabel gNgrams gQ gPP gPC gLP gLC
349
350 setPhyloLevel :: Int -> PhyloLevel -> PhyloLevel
351 setPhyloLevel lvl (PhyloLevel (periodId, lvl') lvlGroups)
352 = PhyloLevel (periodId, lvl) lvlGroups'
353 where
354 lvlGroups' = map (\g -> setGroupIdLvl lvl g) lvlGroups
355
356 copyPhyloLevel :: Int -> [PhyloLevel] -> [PhyloLevel]
357 copyPhyloLevel lvl l = (setPhyloLevel lvl (head l)) : l
358
359 alterLvl :: Int -> [PhyloPeriod] -> [PhyloPeriod]
360 alterLvl lvl l = map (\p -> PhyloPeriod (_phylo_periodId p) (copyPhyloLevel lvl $ _phylo_periodLevels p)) l
361
362 phyloWithGroups0 :: Phylo
363 phyloWithGroups0 = updatePhyloByLevel Level_0 phyloWithGroupsm1
364
365
366 ------------------------------------------------------------------------
367 -- | STEP 4 | -- Build level -1
368
369
370 docsToLevel :: (Date, Date) -> Corpus -> Phylo -> PhyloLevel
371 docsToLevel k v p = PhyloLevel
372 (k,(-1))
373 (map (\x -> initGroup [snd x] (snd x) (fst x) (-1) (fst k) (snd k) p)
374 $ zip [1..]
375 $ (nub . concat)
376 $ map (words . text) v)
377
378 corpusToPhyloPeriod :: Map (Date,Date) Corpus -> Phylo -> [PhyloPeriod]
379 corpusToPhyloPeriod corpus p = map (\x -> PhyloPeriod (fst x) [(snd x)]) $ zip (keys mapLvl) (elems mapLvl)
380 where
381 mapLvl :: Map (Date,Date) PhyloLevel
382 mapLvl = mapWithKey (\k v -> docsToLevel k v p) corpus
383
384
385 -- | To update a Phylo by adding a new PhyloLevel to each PhyloPeriod
386 updatePhyloByLevel :: Level -> Phylo -> Phylo
387 updatePhyloByLevel lvl (Phylo pDuration pNgrams pPeriods)
388 = case lvl of
389
390 Level_m1 -> Phylo pDuration pNgrams pPeriods'
391 where pPeriods' = (corpusToPhyloPeriod phyloTerms (Phylo pDuration pNgrams pPeriods)) ++ pPeriods
392
393 Level_0 -> Phylo pDuration pNgrams pPeriods'
394 where pPeriods' = alterLvl 0 pPeriods
395
396 Level_1 -> fisToPhyloLevel phyloFisFiltered (Phylo pDuration pNgrams pPeriods)
397
398 Level_N -> alterPhyloPeriods (\x -> x) (Phylo pDuration pNgrams pPeriods)
399
400 _ -> panic ("error level to be defined")
401
402
403
404 -- | To update a Phylo by adding a new PhyloLevel to each PhyloPeriod
405 updatePhyloByLevel' :: Level -> Phylo -> Phylo
406 updatePhyloByLevel' lvl p
407 = case lvl of
408 Level_m1 -> appendPhyloPeriods (corpusToPhyloPeriod phyloTerms p) p
409 _ -> panic ("error level to be defined")
410
411 phyloWithGroupsm1 :: Phylo
412 phyloWithGroupsm1 = updatePhyloByLevel Level_m1 phylo
413
414
415 ------------------------------------------------------------------------
416 -- | STEP 3 | -- Split the Corpus into Periods and reduce each Document as a list of Ngrams
417
418
419 phyloTerms :: Map (Date, Date) [Document]
420 phyloTerms = toPeriodes date 5 3 $ cleanCorpus cleanedActants phyloCorpus
421
422 toPeriodes :: (Ord date, Enum date) => (doc -> date)
423 -> Grain -> Step -> [doc] -> Map (date, date) [doc]
424 toPeriodes _ _ _ [] = panic "Empty corpus can not have any periods"
425 toPeriodes f g s es = Map.fromList $ zip hs $ map (inPeriode f es) hs
426 where
427 hs = steps g s $ both f (head es, last es)
428 --------------------------------------------------------------------
429 -- | Define overlapping periods of time by following regular steps
430 inPeriode :: Ord b => (t -> b) -> [t] -> (b, b) -> [t]
431 inPeriode f' h (start,end) =
432 fst $ List.partition (\d -> f' d >= start && f' d <= end) h
433 --------------------------------------------------------------------
434 -- | Find steps of linear and homogenous time of integer
435 steps :: (Eq date, Enum date) => Grain -> Step -> (date, date) -> [(date, date)]
436 steps s' o' (start,end) = map (\l -> (head l, last l))
437 $ chunkAlong s' o' [start .. end]
438
439 cleanCorpus :: MapList -> Corpus -> Corpus
440 cleanCorpus ml = map (\(Document d t) -> Document d (unwords $ filter (\x -> elem x ml) $ monoTexts t))
441
442
443 ------------------------------------------------------------------------
444 -- | STEP 2 | -- Find some Ngrams (ie: phyloGroup of level -1) out of the Corpus & init the phylo
445
446
447 phylo = Phylo (both date $ (last &&& head) phyloCorpus) phyloNgrams []
448
449 phyloNgrams :: PhyloNgrams
450 phyloNgrams = Vector.fromList cleanedActants
451
452 cleanedActants :: [Ngrams]
453 cleanedActants = map toLower actants
454
455 actants :: [Ngrams]
456 actants = [ "Cleopatre" , "Ptolemee", "Ptolemee-XIII", "Ptolemee-XIV"
457 , "Marc-Antoine", "Cesar" , "Antoine" , "Octave" , "Rome"
458 , "Alexandrie" , "Auguste" , "Pompee" , "Cassius" , "Brutus"]
459
460
461 ------------------------------------------------------------------------
462 -- | STEP 1 | -- Get a corpus of Documents
463
464
465 phyloCorpus :: Corpus
466 phyloCorpus = map (\(d,t) -> Document d t) exampleDocuments
467
468 exampleDocuments :: [(Date, Text)]
469 exampleDocuments = List.sortOn fst [ (-51,"Cleopatre règne sur l’egypte entre 51 et 30 av. J.-C. avec ses frères-epoux Ptolemee-XIII et Ptolemee-XIV, puis aux côtes du general romain Marc-Antoine. Elle est celèbre pour avoir ete la compagne de Jules Cesar puis d'Antoine, avec lesquels elle a eu plusieurs enfants. Partie prenante dans la guerre civile opposant Antoine à Octave, elle est vaincue à la bataille d'Actium en 31 av. J.-C. Sa defaite va permettre aux Romains de mener à bien la conquête de l’egypte, evenement qui marquera la fin de l'epoque hellenistique."), (-40,"Il existe relativement peu d'informations sur son sejour à Rome, au lendemain de l'assassinat de Cesar, ou sur la periode passee à Alexandrie durant l'absence d'Antoine, entre -40 et -37."), (-48,"L'historiographie antique lui est globalement defavorable car inspiree par son vainqueur, l'empereur Auguste, et par son entourage, dont l'interêt est de la noircir, afin d'en faire l'adversaire malfaisant de Rome et le mauvais genie d'Antoine. On observe par ailleurs que Cesar ne fait aucune mention de sa liaison avec elle dans les Commentaires sur la Guerre civile"), (-69,"Cleopatre est nee au cours de l'hiver -69/-686 probablement à Alexandrie."), (-48,"Pompee a en effet ete le protecteur de Ptolemee XII, le père de Cleopatre et de Ptolemee-XIII dont il se considère comme le tuteur."), (-48,"Ptolemee-XIII et Cleopatre auraient d'ailleurs aide Pompee par l'envoi d'une flotte de soixante navires."), (-48,"Mais le jeune roi Ptolemee-XIII et ses conseillers jugent sa cause perdue et pensent s'attirer les bonnes graces du vainqueur en le faisant assassiner à peine a-t-il pose le pied sur le sol egyptien, près de Peluse, le 30 juillet 48 av. J.-C., sous les yeux de son entourage."), (-48,"Cesar fait enterrer la tête de Pompee dans le bosquet de Nemesis en bordure du mur est de l'enceinte d'Alexandrie. Pour autant la mort de Pompee est une aubaine pour Cesar qui tente par ailleurs de profiter des querelles dynastiques pour annexer l’egypte."), (-48,"Il est difficile de se prononcer clairement sur les raisons qui ont pousse Cesar à s'attarder à Alexandrie. Il y a des raisons politiques, mais aussi des raisons plus sentimentales (Cleopatre ?). Il tente d'abord d'obtenir le remboursement de dettes que Ptolemee XII"), (-46,"Les deux souverains sont convoques par Cesar au palais royal d'Alexandrie. Ptolemee-XIII s'y rend après diverses tergiversations ainsi que Cleopatre."), (-47,"A Rome, Cleopatre epouse alors un autre de ses frères cadets, à Alexandrie, Ptolemee-XIV, sur l'injonction de Jules Cesar"), (-46,"Cesar a-t-il comme objectif de montrer ce qu'il en coûte de se revolter contre Rome en faisant figurer dans son triomphe la sœur de Cleopatre et de Ptolemee-XIV, Arsinoe, qui s'est fait reconnaître reine par les troupes de Ptolemee-XIII ?"), (-44,"Au debut de l'annee -44, Cesar est assassine par Brutus. Profitant de la situation confuse qui s'ensuit, Cleopatre quitte alors Rome à la mi-avril, faisant escale en Grèce. Elle parvient à Alexandrie en juillet -44."), (-44,"La guerre que se livrent les assassins de Cesar, Cassius et Brutus et ses heritiers, Octave et Marc-Antoine, oblige Cleopatre à des contorsions diplomatiques."), (-41,"Nous ignorons depuis quand Cleopatre, agee de 29 ans en -41, et Marc-Antoine, qui a une quarantaine d'annees, se connaissent. Marc-Antoine est l'un des officiers qui ont participe au retablissement de Ptolemee XII. Il est plus vraisemblable qu'ils se soient frequentes lors du sejour à Rome de Cleopatre."), (-42,"Brutus tient la Grèce tandis que Cassius s'installe en Syrie. Le gouverneur de Cleopatre à Chypre, Serapion, vient en aide à Cassius."), (-42,"Cassius aurait envisage de s'emparer d'Alexandrie quand le 'debarquement' en Grèce d'Antoine et d'Octave l'oblige à renoncer à ses projets")]
470