├── Experiments ├── Cinema │ ├── Data │ │ ├── DUNKIRK.txt │ │ ├── GRAVITY.txt │ │ ├── INTERSTELLAR.txt │ │ ├── KILL_BILL_VOLUME_1.txt │ │ ├── KILL_BILL_VOLUME_2.txt │ │ ├── THE_MARTIAN.txt │ │ └── TITANIC.txt │ └── cinema.py ├── Disk_to_annulus │ ├── disk_to_annulus.py │ └── disk_to_annulus_dependence_on_n.py ├── Fragmented_hypercube │ ├── fragmented_hypercube.py │ └── fragmented_hypercube_dependence_on_n.py ├── Noise │ ├── noise.py │ └── noise_level.py └── computation_time.py ├── Optimization ├── algorithm.py ├── frankwolfe.py ├── projectedascent.py ├── sinkhorn.py └── sinkhornGPU.py ├── README.md ├── SRW.py ├── demo.ipynb └── utils.py /Experiments/Cinema/Data/DUNKIRK.txt: -------------------------------------------------------------------------------- 1 | 2 | DUNKIRK 3 | BLACK SCREEN: 4 | WATER slaps hollow metal, metal knocks CREAKING WOOD... Super title: 5 | 1 "DUNKIRK" 1 FADE IN: 6 | Paper. Falling like snow. Six young, filthy Tommys raise their heads along a deserted street, checking rubbish bins, windows... One crouches to check a coiled garden hose. He tries the tap- nothing... 7 | TITLE 1 8 | THE ENEMY HAVE DRIVEN THE BRITISH AND FRENCH ARMIES TO THE SEA. 9 | One TOMMY plucks paper from the air... propaganda leaflets showing their position... "YOU ARE SURROUNDED"... 10 | TITLE 2 11 | TRAPPED AT DUNKIRK, THEY AWAIT THEIR FATE. 12 | He wads the leaflets up, crouches, drops his trousers... the Tommy with the hose carefully lifts each side... 13 | TITLE 3 14 | HOPING FOR DELIVERANCE. 15 | He gets a tiny DRIBBLE of water which he licks from the nozzle- TITLE 4 16 | FOR A MIRACLE. 17 | BLAMBLAMBLAM!- Tommy JOLTS, grabs his trousers- all six RACE away from us, towards a FENCE twenty yards away- one by one FIVE are SHOT DOWN- the survivor CLIMBS the fence- 18 | Gunfire BURSTS through the fence, TEN FEET AWAY- 19 | Tommy tries to RELOAD his rifle- fingers STRUGGLING with the magazine, training forgotten- 20 | Gunfire SPLINTERS the fence, FIVE FEET AWAY- 21 | Tommy THRUSTS his index finger into the breach of his rifle AGAIN AND AGAIN, scraping skin- a round JUMPS into the chamber- 22 | GUNFIRE THREE FEET AWAY- 23 | Tommy tries once, twice- slides the bolt FORWARD- 24 | (CONTINUED) 25 | 1. 26 | 27 | 2. 28 | 1 CONTINUED: 1 29 | GUNFIRE RIGHT NEXT TO HIM- 30 | Tommy SPINS AROUND, FIRES BLIND until empty, SCRAMBLES out the back- 31 | He RACES down NARROW DUNKIRK STREETS. BREATHING. Kit JANGLING... building after building... he rounds a corner- 32 | BLAM! Bullets hit dirt and bricks near him- the street ahead is barricaded, manned by FRENCH TROOPS- 33 | TOMMY ANGLAIS! ANGLAIS! 34 | The French stop firing and wave him through. 35 | He SCRAMBLES over their sandbag barricade, taking in their dirty, frightened faces as he passes... 36 | A FRENCH SOLDIER grabs him- 37 | FRENCH SOLDIER Allez, Anglais. 38 | Tommy's mouth opens at the man's BITTERNESS. 39 | FRENCH SOLDIER (contempt) (CONT'D) 40 | Bon voyage. 41 | He SHOVES Tommy down the street behind their protection. 42 | GUNFIRE behind- Tommy TAKES OFF AGAIN- HURTLING down the dark street, heading towards the BLAZING LIGHT of- 43 | 2 EXT. BEACH AT MALO LES BAINS - CONTINUOUS 2 44 | The LONGEST, WIDEST BEACH he's ever seen, sunlight DAZZLING off the water- 45 | Endless dark FENCES snaking across the sand and out into the water- 46 | Tommy SQUINTS- not fences- lines of MEN- HUNDREDS OF THOUSANDS OF MEN... 47 | Tommy looks around, clutching his stomach. He CLAMBERS over a dune, FEVERISHLY undoing his belt, dropping trousers and SQUATTING before he realizes- 48 | He's not alone- 49 | Another soldier, British army shirt undone, sweating with the labour of BURYING A BODY. This is GIBSON. 50 | The other man notices Tommy, but barely pauses. Tommy finishes, pulls up his trousers and moves towards him. 51 | (CONTINUED) 52 | 53 | 3. 54 | 2 CONTINUED: 2 55 | Tommy helps scoop sand over the body. 56 | Tommy notices the corpse's STOCKINGED FEET, then watches Gibson stoop to TIE HIS BOOTS... 57 | Gibson looks up at him. Tommy shrugs, gestures for Gibson's water can. Gibson hands it over and Tommy takes a SWIG, carefully CATCHING drops in his hand, then LICKING them off his palm. 58 | Tommy leaves Gibson buttoning his shirt and heads back onto the beach. 59 | There are DESTROYERS out on the water, too far to reach. 60 | Tommy wanders down to join one of the long, snaking lines which extends into the sea, soldiers up to their CHESTS in water, WAITING PATIENTLY FOR SHIPS WHICH DO NOT MOVE. 61 | The man at the back turns to Tommy, unwelcoming. Points at his own insignia. 62 | MAN Grenadiers, Mate. 63 | Tommy moves off. Looks around at other impossibly long lines. At the unattainable ships. Futile. 64 | A line of STRETCHER BEARERS comes past, carrying wounded men along the beach towards the harbour... 65 | Looking where they're headed, Tommy sees a LONG, NARROW BREAKWATER extending out into the sea, packed with soldiers. A HOSPITAL SHIP at the end of it. 66 | This breakwater extends a kilometer into the sea. It is called THE MOLE. 67 | Super title: 68 | 1. THE MOLE 69 | one week 70 | Tommy becomes aware of the sound of DISTANT AIRCRAFT. Soldiers peer up into the sky... 71 | MALE VOICE (O.S.) DIVE BOMBERS! 72 | Tommy spots the distinctive KINKED WINGS of the notorious STUKA DIVE BOMBER- its NIGHTMARISH HOWL RISING as it picks up speed, DIVING at the beach... 73 | The lines of men INSTANTLY VANISH- soldiers SCATTERING back to the dunes, BURROWING into the sand... the first BOMBS lift sand into the air- 74 | (CONTINUED) 75 | 76 | 4. 77 | 2 CONTINUED: (2) 2 78 | The stretcher bearers put down their loads, lying across them, protecting them as the area is HAMMERED... 79 | The first STUKA pulls out of its dive, revealing TWO MORE STUKAS DIVING- there are NINE MORE about to follow... 80 | Tommy sees a soldier lying ON HIS BACK, RIFLE AIMED AT THE SKY, FIRING DEFIANTLY, DESPERATELY at the attacking plane... the ground around him LIFTS into the air with the second wave of bombs. 81 | Tommy buries his face in the sand as the bombs BLAST AND BLAST AND BLAST- 82 | The explosions stop. Tommy lifts his head. BOOM- ANOTHER WAVE OF BOMBS EXPLODES IN SERIES UP THE BEACH. Then, finally, quiet. Tommy rises... 83 | The stretcher bearers, back on their feet, lift their burdens (four bearers per stretcher, one at each corner). 84 | Several stretchers are left behind on the sand. 85 | Soldiers on the beach watch in despair as one of the DESTROYERS is slipping below the water, smoke billowing. 86 | MALE VOICE (CONT'D) WHERE'S THE BLOODY AIR FORCE?! 87 | CUT TO: 88 | 3 EXT. ENGLISH COAST, WEYMOUTH HARBOUR - MORNING 3 89 | A LANKY YOUTH runs down to the masts of 90 | He RACES along the wooden dock, jumping rushes to a large yacht, the MOONSTONE. 91 | the crowded harbour... over ropes as he 92 | SUPER TITLE: 93 | 2. THE SEA 94 | one day 95 | The Youth, GEORGE (17), leaps from the dock into the well- 96 | Two NAVAL OFFICERS emerge from the cabin, pushing past. George watches them go, confused... 97 | Mr.Dawson (50's, civilian dress) hands George a stack of china plates and ducks back inside. 98 | A second young man, PETER (19), emerges, carrying boxes- 99 | PETER 100 | Navy's requisitioned her- there's 101 | some men across the channel, at Dunkirk, need taking off. 102 | (MORE) 103 | (CONTINUED) 104 | 105 | 5. 106 | 3 CONTINUED: 3 107 | PETER (CONT'D) (points at dock) 108 | They told us to strip her and load those life jackets. 109 | George looks along at the dock. At a pile of HUNDREDS of life jackets. George looks at Peter. Surprised. 110 | GEORGE 111 | Some men? 112 | PETER 113 | Navy'll be back in an hour. My dad 114 | wants to be ready before then... 115 | CUT TO: 116 | 4 EXT. SKY - DAY 4 117 | MOVING through BILLOWY PEAKS, three sleek, beautiful SPITFIRES streak into frame. Elegant. In confident formation. 118 | SUPER TITLE: 119 | 3. THE AIR 120 | one hour 121 | 5 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 5 122 | The pilot, FARRIER, has a light touch on the controls. He checks his left and right, scanning the skies. 123 | VOICE ON RADIO Check fuel, Fortis 1 and 2. 124 | Farrier reaches forward to his FUEL GAUGE, pushes the button beside it- the needle SHOOTS up to 3/4 FULL. 125 | FARRIER 126 | 70 gallons. 127 | 6 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 6 128 | The pilot, COLLINS, checks his fuel gauge- 129 | COLLINS 130 | 68 gallons, Fortis Leader. 131 | FORTIS LEADER (over radio) Stay down at 500ft to leave fuel for 132 | 40 minutes fighting time over Dunkirk. 133 | COLLINS 134 | Understood. Vector 128, angels point 135 | five. 136 | 137 | 7 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 7 Farrier checks his chart. 138 | FORTIS LEADER 139 | Keep an eye on that gauge, even when 140 | it gets lively- save enough to get back. 141 | With a glance at his fuel gauge, Farrier pulls on the stick- 142 | 8 EXT. SKY - CONTINUOUS 8 143 | The three planes bank left in perfect harmony as we- 144 | CUT TO: 145 | 9 EXT. BEACH AT LA PANNE - LATE AFTERNOON 9 146 | Tommy looks down at several patients on stretchers left behind, bearers dead or disappeared... 147 | ONE OF THEM GROANS. STILL ALIVE. Tommy looks around- Gibson is there. 148 | They GRAB the stretcher and HUSTLE down the beach towards the mole... 149 | 10 EXT. BASE OF THE MOLE - CONTINUOUS 10 150 | A WARRANT OFFICER tries to keep order as men line up to start the shuffle out along the 8 FOOT WIDE CONCRETE MOLE. 151 | The LINE OF STRETCHER BEARERS approaches... 152 | From the base all you can see is the the backs of helmeted heads queuing out onto the narrow breakwater. 153 | The Warrant Officer sees the stretchers, waves them past- 154 | WARRANT OFFICER 155 | Along the mole. All the way, she's 156 | leaving- A SHIPS'S WHISTLE- 157 | WARRANT OFFICER (CONT'D) 158 | That's it- 159 | (he turns) 160 | MAKE WAY! MAKE WAY! 161 | The stretcher bearers squeeze past... 162 | 6. 163 | 164 | 11 EXT. BEACH - CONTINUOUS 165 | 11 166 | Tommy and Gibson hear the SHIP'S WHISTLE. They with the stretcher, heading for the base of the 167 | 12 EXT. BASE OF THE MOLE - CONTINUOUS 168 | The Warrant Officer addresses a group of FRENCH 169 | WARRANT OFFICER 170 | NO FRENCH! NON FRANCAISES- SEULEMENT 171 | ANGLAISES! ENGLISH ONLY, YOU'LL HAVE YOUR OWN SHIPS! 172 | start RUNNING mole... 173 | SOLDIERS- 174 | 12 175 | Tommy and Gibson arrive, panting. The Warrant Officer looks at them. The SHIP'S WHISTLE- the Warrant Officer points up- 176 | WARRANT OFFICER (CONT'D) That's two minutes- you've missed 177 | it. 178 | He turns back to ARGUING with the French... 179 | Tommy PUSHES forward with the stretcher- soldiers try to let him through on the narrow mole- 180 | The Warrant Officer, seeing Tommy, just shakes his head. 181 | 13 EXT. HOSPITAL SHIP - CONTINUOUS 13 182 | Stretchers are loaded up the gangplank onto the deck of the ship, supervised by a Petty Officer. 183 | He checks his watch, then looks along the mole at the remaining stretchers... 184 | 14 EXT. THE MOLE - CONTINUOUS 14 185 | Tommy weaves along the mole, squeezing past the mass of troops jamming the breakwater... 186 | Tommy leans out over the edge where the RAIL IS MISSING, a 20ft drop to the churning water... 187 | Gibson follows, echoing Tommy's route and footing... 188 | 15 EXT. HOSPITAL SHIP - CONTINUOUS 15 189 | The last of the line of stretchers is carefully, awkwardly raised up from the mole onto the deck of the ship. The Petty Officer speaks urgently to the last stretcher bearer. 190 | PETTY OFFICER 191 | Last? 192 | The Stretcher Bearer NODS, too breathless to speak, then follows his colleagues BACK DOWN OFF THE SHIP- 193 | (CONTINUED) 194 | 7. 195 | 196 | 8. 197 | 15 CONTINUED: 15 198 | An EXPLOSION hits the water nearby- 199 | Everyone hits the deck as shells IMPACT the water. 200 | 16 EXT. THE MOLE - CONTINUOUS 16 201 | Tommy is PULLED UP SHORT as Gibson STUMBLES- 202 | A 109 STRAFES the length of the mole with gunfire- soldiers hit the deck, several are HIT... 203 | Gibson STRUGGLES up- 204 | CUT TO: 205 | 17 EXT. WEYMOUTH HARBOUR - DAY 17 206 | Peter and George RUSH things off the boat- then start loading the orange life preservers Mr.Dawson looks up from his charts to see NAVAL OFFICERS AND CREW coming along the harbor, assigning crew members to boats... 207 | Peter follows his gaze- 208 | 18 INT. MOONSTONE - CONTINUOUS 18 209 | Peter bursts into the cabin, stacking life vests- 210 | 19 EXT. MOONSTONE - CONTINUOUS 19 211 | The pile of life vests on the dock SHRINKS... Mr.Dawson watches the Naval men coming closer... 212 | CUT TO: 213 | 20 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 20 214 | Farrier lightly brushes his fingers over the dashboard. 215 | COLLINS (over radio) Dunkirk's so far, why can't they 216 | load at Calais? 217 | Farrier looks over at his wing mate, COLLINS (Fortis 2) 218 | FORTIS LEADER (over radio) The enemy had something to say about 219 | it. 220 | 21 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS Collins SCANS the skies above... 221 | COLLINS 222 | Down here we're sitting ducks. 223 | 21 224 | (CONTINUED) 225 | 226 | 9. 227 | 21 CONTINUED: 21 228 | FORTIS LEADER (over radio) Keep 'em peeled. They'll come out 229 | of the sun. 230 | 22 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 22 Farrier looks around into the blinding sun... 231 | CUT TO: 232 | 23 EXT. HOSPITAL SHIP - CONTINUOUS 23 233 | The Petty Officer barks orders at the crew- 234 | PETTY OFFICER 235 | Man the bow line! Ready on the stern! 236 | Troops stuck down on the mole below look resentfully at the ship preparing to depart. One soldier calls up- 237 | SOLDIER Any more room? 238 | The Petty Officer GLARES down at him. 239 | 24 EXT. THE MOLE - CONTINUOUS 24 240 | Tommy breaks through a tight crowd of soldiers and STOPS. 241 | In front of him is a jagged chasm. One NARROW PLANK laid across it. The drop is fifteen feet to rocks and concrete below- 242 | The SHIP WHISTLE SOUNDS- Tommy stares- 243 | SOLDIER Take a run at it! 244 | Tommy glances at the soldier who spoke. Looks back at Gibson- 245 | TOMMY One, two, three! 246 | Tommy BOLTS across, pure concentration, the plank BOWING and BOUNCING as he crosses the middle, Gibson following- Tommy's foot SLIPS- he almost goes over, rights himself- 247 | Helpful arms GRAB them as they hit the other side, a couple of CHEERS from the crowd- Tommy PLOUGHS ON- 248 | 25 EXT. HOSPITAL SHIP - CONTINUOUS 25 249 | Tommy passes the stretcher bearers coming back down the mole, one of them moves to help but Tommy SHAKES his head, pushing past... 250 | The Petty Officer gestures at his men to pull the gangplanks- (CONTINUED) 251 | 252 | 25 CONTINUED: 253 | 25 254 | PETTY OFFICER PULL THE GANGPLANKS! 255 | Tommy and Gibson arrive at the end of the mole- TOMMY 256 | Oi! 257 | A gangplank is shoved back down- 258 | They STRUGGLE up it with the stretcher... 259 | When they make it to the deck they practically drop their burden, GASPING for breath. Orderlies takes the stretcher below. 260 | Tommy and Gibson look around for a place to perch, catching their breaths... 261 | CUT TO: 262 | 26 EXT. MOONSTONE - CONTINUOUS 26 263 | Mr.Dawson sees the Naval Officers stepping onto their dock- 264 | MR.DAWSON Ready on the stern line. 265 | George HOPS onto the dock, unties the stern line. STOPS. Looks at the approaching Officers. Then back to Mr.Dawson- 266 | GEORGE 267 | Aren't you waiting for the navy? 268 | Mr.Dawson starts the engine. Peter jumps down onto the boat with the bow rope... 269 | MR.DAWSON 270 | They've asked for the Moonstone, 271 | they'll have her. With her Captain. 272 | PETER And his son. 273 | The boat moves off- Peter looks to catch the line from George- 274 | PETER (CONT'D) Thanks for the help, George. 275 | Who, instead, JUMPS onto the stern, to Peter's surprise. 276 | PETER (CONT'D) You know where we're going? 277 | France. 278 | GEORGE 279 | (CONTINUED) 280 | 10. 281 | 282 | 11. 283 | 26 CONTINUED: 26 284 | MR.DAWSON Into war, George. 285 | GEORGE I'll be useful, sir. 286 | Mr.Dawson looks at George. Pushes the throttle forward and they motor out of the harbour into the English Channel... 287 | CUT TO: 288 | 27 INT. COCKPIT, SPITFIRE 2 - DAY 27 289 | Collins spots something- 290 | 28 INT. COCKPIT, SPITFIRE 1 - DAY 28 291 | Farrier SPOTS the ME109 COMING OUT OF THE SUN- 292 | FARRIER Bandit- eight o'clock. 293 | FORTIS LEADER (over radio) 294 | Break. 295 | 29 EXT. SKY OVER ENGLISH CHANNEL - CONTINUOUS 29 296 | The three Spitfires dart away from each other- the German plane takes the left one (Collins), HURTLING down- 297 | 30 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 30 Collins dives, ROLLING, glancing back- 298 | COLLINS 299 | He's on me! 300 | 31 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 31 301 | Farrier banks around, lining up on Collins' pursuer- 302 | FARRIER And I'm on him- 303 | CUT TO: 304 | 32 EXT. HOSPITAL SHIP - EVENING 32 305 | Tommy and Gibson shuffle around the deck, looking for a spot to settle... 306 | The Able Seaman manning the gangplank calls over- 307 | ABLE SEAMEN You two, get a shift on! 308 | Tommy reluctantly follows Gibson onto the plank. 309 | 310 | 33 EXT. THE MOLE - CONTINUOUS 33 311 | As he shuffles down the gangplank he looks over at the THOUSANDS queuing on the mole... 312 | A SECOND LIEUTENANT on the mole WAVES Tommy along- 313 | SECOND LIEUTENANT Off you go! Back up the line! 314 | As Tommy steps off the plank he hears a noise- 315 | Gibson, finger to his lips, "shush", is crouched in the crisscross structure below the mole where he can't be seen by the officers on top. He beckons Tommy to join him... 316 | PETTY OFFICER 317 | That last barrage damaged the rudder! 318 | The Second Lieutenant TURNS to the Petty Officer- Tommy slips down beside Gibson- 319 | SECOND LIEUTENANT 320 | Tie up again while we try to fix it. 321 | They settle in on the beams just above the water line... 322 | CUT TO: 323 | 34 EXT. MOONSTONE, ENGLISH CHANNEL - MORNING 34 324 | Mr.Dawson comes to the back of the well, steer from outside. Peter at his side. looks across at several Naval Vessels on 325 | Suddenly he SPOTS a BOMBER overhead- GEORGE 326 | Mr.Dawson! 327 | Mr.Dawson's eyes don't leave his course- 328 | MR.DAWSON One of ours, George. 329 | George looks up as the plane, a Blenheim 330 | fits the TILLER, to George, on the bow, the same course. 331 | Looking down to his left- a FISHING TRAWLER bobbing along. Further back down the convoy his sees a THAMES PADDLE STEAMER. 332 | A Destroyer approaches from the opposite direction. As George PEERS, he starts to make out shapes of men on the decks. 333 | The Destroyer passes close enough that George can see that the boat is PACKED with soldiers. Weary, bedraggled, dispirited soldiers. George STARES at the haunted faces. 334 | bomber passes over. 335 | (CONTINUED) 336 | 12. 337 | 338 | 34 CONTINUED: 339 | 34 340 | As the Moonstone rides over the WAKE of the Destroyer, an OMINOUS BOOM REVERBERATES in the distance. Too sudden for thunder, the boom multiplies into a distant barrage... 341 | Mr.Dawson comes forwards, drawn by the sound. He stares at the horizon- distant BLACK SMOKE precisely where they're headed. More BOOMS. Mr.Dawson looks at George. Who is SCARED. He puts his hand on his shoulder. Nods reassuringly. 342 | CUT TO: 343 | 35 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 344 | Farrier concentrates, trying to angle 345 | of the ME 109 ahead... but the German 346 | out of his sights, turning right, pulling g's, rolling... 347 | FARRIER 348 | On my mark- draw him left, Fortis 349 | 2... 3,2,1, mark- 350 | 35 351 | 36 INT. COCKPIT, 352 | Collins pulls STREAKS PAST- 353 | 37 INT. COCKPIT, 354 | SPITFIRE 2 - CONTINUOUS 36 hard left, rolling up and left as TRACER FIRE 355 | SPITFIRE 1 - CONTINUOUS 37 356 | Farrier watches the ME 109 cut left to follow Collins- he pushes the button on his stick to STRAFE the plane with his cannons... SMOKE starts trailing from the German plane- 357 | FARRIER 358 | Clear. 359 | 38 EXT. SKY OVER ENGLISH CHANNEL - CONTINUOUS 38 360 | The ME 109 trails heavy smoke as it tips towards the water... 361 | 39 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 39 362 | Collins straightens out- tries to look back- COLLINS 363 | Is he down? 364 | 40 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 40 365 | Farrier watches the ME 109 SMASH into the water, breaking up in a fiery mess- 366 | FARRIER Down for the count- 367 | TRACER FIRE SMASHES into Farrier's plane, SPARKING inside and out- Farrier banks HARD RIGHT as a second 109 STREAKS away- he straightens up- 368 | his plane at the tail plane keeps pulling 369 | (CONTINUED) 370 | 13. 371 | 372 | 14. 373 | 40 CONTINUED: 40 374 | FARRIER (CONT'D) Fortis leader, one bandit down... 375 | (nothing) 376 | Fortis Leader, do you read? 377 | Nothing. 378 | Farrier looks around- spots a Spitfire- 379 | FARRIER (CONT'D) Fortis 2, I have you to port- no 380 | eyes on Fortis leader. Over. 381 | COLLINS (over radio) Understood, Fortis 1. Orbit for a 382 | look... 383 | Farrier looks all around as he pulls right on the stick... CUT TO: 384 | 41 EXT. THE MOLE - EVENING 41 Eerie quiet. 385 | Tommy and Gibson sit in the structure, UNSEEN, listening... COMMANDER BOLTON checks progress on board the hospital ship. 386 | COMMANDER BOLTON How long, Lieutenant? 387 | LIEUTENANT 388 | We need to run a new cable, sir. 389 | They're scrambling. 390 | Commander Bolton turns to Colonel Winnant, the army representative. 391 | COMMANDER BOLTON Colonel, you're going to have to 392 | decide how many more wounded to evacuate... one stretcher takes the space of seven standing men. 393 | Colonel Winnant takes this in. 394 | Tommy crouches lower as he sees a LAUNCH approach... 395 | A HIGH RANKING OFFICER is helped up the ladder onto the mole. 396 | COMMANDER BOLTON (salutes) (CONT'D) Rear Admiral. 397 | Commander. 398 | REAR ADMIRAL (MORE) 399 | (CONTINUED) 400 | 401 | 41 CONTINUED: 402 | 41 403 | REAR ADMIRAL (CONT'D) (to Colonel Winnant) 404 | At ease, Colonel. How's the perimeter? 405 | Colonel Winnant GESTURES towards the smoke-shrouded town- 406 | COLONEL WINNANT Shrinking every day. But between 407 | our rear guard and the French... we're holding the line. And the enemy tanks've stopped. 408 | COMMANDER BOLTON 409 | Why? 410 | COLONEL WINNANT 411 | Waste precious tanks, when you can 412 | pick us off from the air, like fish in a barrel? 413 | COMMANDER BOLTON 414 | How long does London expect the army 415 | to hold out before we make terms? The Rear Admiral looks sharply at the idea. 416 | REAR ADMIRAL 417 | Make terms? They're not stopping 418 | here. We need to get our army back. The Rear Admiral points across the dark water... 419 | REAR ADMIRAL (CONT'D) Britain's next. Then the world. 420 | Commander Bolton puts his field glasses to his face- 421 | COMMANDER BOLTON Christ, you can almost see it from 422 | here... 423 | What? 424 | COLONEL WINNANT 425 | COMMANDER BOLTON 426 | Home. 427 | (turns to the town) 428 | What about the French? 429 | REAR ADMIRAL 430 | Publicly, Churchill's told them "Bras 431 | Dessous" 432 | (off look) 433 | Arm in arm. Leaving together. 434 | (CONTINUED) 435 | 15. 436 | 437 | 41 CONTINUED: (2) 438 | 41 439 | COLONEL WINNANT And privately? 440 | REAR ADMIRAL We need our army back. 441 | COLONEL WINNANT 442 | How many men are they talking about? 443 | REAR ADMIRAL Churchill wants 30,000. Ramsay's 444 | hoping we can give him 45. 445 | Commander Bolton looks out at the mass of humanity. 446 | COMMANDER BOLTON There are 400 thousand men on this 447 | beach, sir. 448 | Down below, Tommy takes this in. Every man for himself. 449 | REAR ADMIRAL 450 | We'll just have to do our best. 451 | Bolton straightens up. 452 | COMMANDER BOLTON Right, this mole stays open at all 453 | costs. 454 | Bolton points at the FUNNEL and MASTS of SUNKEN SHIPS- 455 | COMMANDER BOLTON (CONT'D) We're in range of artillery from the 456 | west- anything else sinks out here the mole's blocked and we're stuffed. 457 | REAR ADMIRAL 458 | Can't we load from the beaches? 459 | COLONEL WINNANT Better than standing out here when 460 | the dive bombers come. COMMANDER BOLTON 461 | Impossible. 462 | The Rear Admiral looks at the lines of men on the beaches. 463 | REAR ADMIRAL 464 | Too shallow. 465 | COMMANDER BOLTON Anything drafting more than three 466 | feet can't get near. (MORE) 467 | (CONTINUED) 468 | 16. 469 | 470 | 17. 471 | 41 CONTINUED: (3) 41 472 | COMMANDER BOLTON (CONT'D) We don't have enough small boats to 473 | ferry men out to the destroyers. The Rear Admiral nods. 474 | REAR ADMIRAL 475 | The mole it is, then, gentlemen. 476 | CUT TO: 477 | 42 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 42 478 | Mr.Dawson is on the bow, peering ahead. The distant smoke is closer, small shapes in the sky move above distant ships, accompanied by thunderous booms... 479 | Much nearer: a shape. A WRECK. Upside down. 480 | Mr.Dawson moves quickly down the yacht to the well and takes the helm, throttling back. He gestures for Peter to head to the bow. 481 | The Moonstone approaches the wreck. BODIES surround the overturned hull. 482 | Crouched on the hull- a SOLDIER. 483 | Mr.Dawson REVERSES the screw, slowing to a crawl. Peter stares out at the SHIVERING SOLDIER. 484 | PETER Can you swim it? 485 | The Shivering Soldier STARES back at Peter. Peter looks back at Mr.Dawson- 486 | PETER (CONT'D) Can you get closer? 487 | Mr.Dawson looks down the side of the boat, considers. 488 | MR.DAWSON Can't risk it! 489 | Mr.Dawson turns to George. 490 | MR.DAWSON (CONT'D) Take Peter a line. 491 | George grabs a coiled rope and heads up to the bow. Peter takes the rope from George- 492 | PETER I'll throw you a line! 493 | (CONTINUED) 494 | 495 | 18. 496 | 42 CONTINUED: 42 497 | The shivering soldier looks up at him, blank. Peter tosses the line. It hits the water several feet in front of the soldier who STARES at it. 498 | Peter gathers the line, then TOSSES it again- 499 | The shivering soldier SPRINGS for it, GRABBING it and HANGING ON as Peter and George reel him in, pulling him around to the stern ladder. 500 | He is too exhausted to make it up the ladder, so they grab his shirt, pulling him into the well. 501 | George grabs a blanket and puts it around the soldier's shoulders. 502 | Mr.Dawson glances at the Soldier, then reverses from the wreck the way he came in, and steers wide around the visible portion of the wreck. 503 | Once the water ahead is open, Mr.Dawson speeds up, heading again for the dark smoke of Dunkirk. 504 | CUT TO: 505 | 43 EXT. SKY ABOVE THE ENGLISH CHANNEL - DAY 43 506 | The two Spitfires arc around the wreckage of the ME 109... 507 | 44 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 44 508 | Farrier SPOTS something- 509 | FARRIER Wreckage below. 510 | He heads low over the wreckage... 511 | COLLINS (over radio) More of the 109? 512 | Farrier banks, looking down- spots a half submerged tail- clearly RAF- 513 | FARRIER 514 | No, it's Fortis Leader, over. 515 | COLLINS 516 | Do you think he got out? 517 | FARRIER Didn't see a 'chute. 518 | Farrier straightens up. Considers. 519 | (CONTINUED) 520 | 521 | 19. 522 | 44 CONTINUED: 44 523 | FARRIER (CONT'D) 524 | Record his position, then set heading 525 | 128, height... 1,000, over. 526 | COLLINS 527 | Vector 128, angels 1. Understood. 528 | Farrier reaches forward, pushes the button by his fuel gauge... NOTHING. 529 | The glass is CRACKED. He TAPS it with his glove. Nothing. 530 | FARRIER 531 | Fortis 2, what's your fuel? 532 | 45 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 45 Collins checks his gauge. 533 | COLLINS Fifty gallons, over. 534 | 46 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 46 Farrier takes this down with a grease pencil... 535 | FARRIER 536 | Keep letting me know- my gauge took 537 | a knock back there, over. 538 | COLLINS Should you turn back? 539 | Farrier methodically checks his other gauges and switches... checks the responsiveness of rudder, aerilons... 540 | FARRIER 541 | I'm confident it's just the gauge. 542 | Farrier glances at his pencil mark, sets the BEZEL on his watch. 543 | He TAPS his gauge one more time. Nothing. 544 | CUT TO: 545 | 47 EXT. THE MOLE - EVENING 47 546 | Bolton watches the Rear Admiral motor away in his launch- the engine noise fading to be replaced by- 547 | A familiar, dreaded sound is building. STUKAS. The men on the mole look up at the sky. 548 | From high above we see how trapped and exposed this line of men stretching a kilometer into the sea really is. 549 | (CONTINUED) 550 | 551 | 20. 552 | 47 CONTINUED: 47 553 | Restless, the soldiers look behind and in front. There's simply nowhere to go. The awful whine builds. Then changes pitch as the bombers go into their DIVE. 554 | BOOMBOOMBOOMBOOMBOOM! The bombs impact the sea either side of the mole- soldiers crouch as low as they can- 555 | The onslaught is ENDLESS, TERRIBLE and INESCAPABLE... 556 | BOOM! A direct hit to the Hospital Ship- 557 | The Stukas have gone. 558 | Screams and shouts- people start JUMPING OVER THE SIDE of the HOSPITAL SHIP onto the mole... 559 | VOICES 560 | She's going down! SHE'S GOING UNDER! 561 | Commander Bolton shouts at the men manning the lines- 562 | COMMANDER BOLTON CUT HER LOOSE! 563 | The crew are jumping off the side, the burning ship is SINKING- 564 | SUB-LIEUTENANT What about the wounded? 565 | COMMANDER BOLTON 566 | Cut her loose, and push her off! We 567 | can't let her sink at the mole! The men cast her off and PUSH her off- 568 | Crew members and orderlies LEAP from the deck into the water- 569 | The bow of the BLAZING, SINKING ship drifts away from the mole... 570 | Tommy and Gibson pull soldiers up onto the beams of the mole... 571 | As the bow comes around, the stern SCRAPES along the wooden pilings, SPLINTERING them in its path- 572 | A FLAILING SOLDIER is in its path, trying to swim free- The STEEL HULK is about to CRUSH HIM- 573 | Tommy GRABS him by the shoulders and YANKS with all his might, pulling him clear just as the hull GRINDS against the wood- 574 | Tommy looks down on the breathless, wet soldier. The wet Soldier focuses on Tommy. 575 | THIS IS ALEX. He nods thanks. Tommy nods back. (CONTINUED) 576 | 577 | 21. 578 | 47 CONTINUED: (2) 47 579 | Commander Bolton WATCHES the ship slip down into the waves. CUT TO: 580 | 48 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 48 581 | Mr.Dawson is back at the helm. The Shivering Soldier sits in the well, blanket over his shoulders. Staring at the deck. George watches him, then leans forward- 582 | GEORGE 583 | Come below- it's out of the wind. 584 | The Shivering Soldier glances at the companionway. Shakes his head. 585 | GEORGE (CONT'D) Really- it's warmer. 586 | George reaches out for the Shivering Soldier's arm- who SMACKS it away- 587 | MR.DAWSON Leave him, George. 588 | George looks up at the Commander. 589 | MR.DAWSON (CONT'D) 590 | He feels safer on deck. You would 591 | too if you'd been bombed- 592 | SHIVERING SOLDIER U-boat. It was a U-boat. 593 | PETER 594 | Get him some tea, George. 595 | George darts downstairs. Useful. 596 | CUT TO: 597 | 49 EXT. SKY OVER ENGLISH CHANNEL - DAY 49 598 | The two Spitfires head towards the massive BLACK SMOKE hanging over the distant port of Dunkirk. 599 | There are many different ships and boats of all sizes in the water in front of them... 600 | 50 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 50 Collins pushes the button to check the fuel gauge- 601 | COLLINS 602 | 40 gallons, Fortis 1. over. 603 | 604 | 51 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 51 Farrier instinctively looks at his gauge. Nothing. 605 | FARRIER 606 | 40 gallons, understood. 607 | Farrier pulls out a grease pencil and notes fuel and time. 608 | FARRIER (CONT'D) 609 | We're about five minutes out- climb 610 | to 2,000. 611 | COLLINS (over radio) That's more fuel. 612 | FARRIER 613 | I don't want to get jumped again. 614 | Get some altitude, dive down on the bastards. Over. 615 | COLLINS (over radio) Understood. Angels two, over. 616 | Farrier pulls back on the stick- 617 | 52 EXT. SKY OVER ENGLISH CHANNEL - CONTINUOUS 52 618 | The Spitfires RISE gloriously into higher air... 619 | CUT TO: 620 | 53 EXT. THE MOLE - EVENING 53 621 | Commander Bolton looks over at the wet soldiers clinging to the understructure of the mole. 622 | COMMANDER BOLTON (O.S.) Right, Highlanders. Let's find you 623 | another ship. 624 | The wet soldiers pull themselves to their feet... 625 | Tommy, watched by Alex, SLIPS into the water, then pulls himself out, dripping. Gibson follows suit. Alex LAUGHS at them... then helps them push into the group... 626 | They follow the wet soldiers up onto the mole, where Bolton's men shepherd them onto a LAUNCH. 627 | 54 EXT. LAUNCH - CONTINUOUS 54 628 | Tommy and Gibson make themselves inconspicuous amongst the Highlanders, eyes down. 629 | As the launch pulls away from the mole, Tommy glances back at the men lining the breakwater. 630 | (CONTINUED) 631 | 22. 632 | 633 | 23. 634 | 54 CONTINUED: 54 635 | The launch motors out of the harbour. 636 | It approaches a DESTROYER, its sheer iron side TOWERING above the launch, as it BOBS up and down alongside. 637 | CARGO NETS are dropped over the side, and the men start to step up onto the rail of the BOBBING ship, waiting for the RHYTHMIC movement towards the iron wall- GRABBING at the rope mesh, STRUGGLING to pull themselves up. 638 | Tommy steps up to the railing, next to an EXHAUSTED soldier who can barely lift himself up- Tommy grabs his shoulder to steady him on the rail as the launch BOUNCES off the iron wall of the destroyer- 639 | They both grab at the net, Tommy climbing up- 640 | The exhausted soldier has not got his feet into the netting, he slips lower... 641 | The gap between the launch and the destroyer shrinks to nothing- 642 | The soldier's legs are CRUSHED between the two oblivious craft- 643 | He SCREAMS- hands PULL him up as the craft separate... 644 | 55 EXT. DESTROYER - CONTINUOUS 55 645 | The men collapse onto the deck in exhausted piles. SAILORS and NURSES urge them to move below decks. 646 | SAILOR 647 | Down below. Come on, mate- 648 | Tommy follows Alex and his mates to a doorway at the head of the stairs down below. A NURSE is standing there. 649 | NURSE 650 | Come on, boys. There's a nice cup 651 | of tea for you down there. This way, come on. 652 | 56 INT. DESTROYER - CONTINUOUS 56 653 | Tommy starts down the stairs- Gibson has stopped at the top, looking down into the stairwell. 654 | NURSE Come on, down you go- 655 | Gibson, shaking his head, steps 656 | Alex sees this- turns to follow hold. They are handed a cup of 657 | back. 658 | Tommy into the crowd in the tea each and a hunk of bread. 659 | 660 | 57 EXT. DESTROYER - CONTINUOUS 57 661 | Out on deck, Gibson sits by the companionway in the gathering dark as the ship gets underway... 662 | 58 INT. HOLD, DESTROYER - CONTINUOUS 58 663 | Down below, Tommy and Alex eat and drink hungrily and gratefully. Between bites, Alex gestures to the stairs. 664 | ALEX 665 | What's wrong with your friend? 666 | Tommy WATCHES THE DOOR TO THE HOLD CLOSE. Takes another bite. Uneasy. 667 | Looks around the hold, PACKED LIKE THE TUBE AT RUSH HOUR. 668 | TOMMY 669 | Looking for a quick way out. In 670 | case we go down. 671 | Tommy and Alex edge through the crowd towards the stairs... 672 | CUT TO: 673 | 59 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 59 674 | George hands the Shivering Soldier a steaming mug of tea. The BOOMS start reverberating again. 675 | The Shivering Soldier glances up. REALIZES SOMETHING... 676 | SHIVERING SOLDIER Where are we going? 677 | MR.DAWSON 678 | Dunkirk. 679 | SHIVERING SOLDIER No, we're going to England! 680 | MR.DAWSON 681 | We have to go to Dunkirk first. 682 | SHIVERING SOLDIER I'M NOT GOING BACK! 683 | Peter watches from the companionway. The Shivering Soldier throws his arm out at the dark cloud on the horizon- 684 | SHIVERING SOLDIER (CONT'D) Look at it! We go there we'll die! 685 | Mr.Dawson looks at the Shivering Soldier. Calm. 686 | (CONTINUED) 687 | 24. 688 | 689 | 25. 690 | 59 CONTINUED: 59 691 | MR.DAWSON 692 | I see your point, son. Take your 693 | tea below and warm up while we plot a course. 694 | The Shivering Soldier considers this. Then takes his blanket and heads down the companionway. Peter helps him down below. 695 | 60 INT. CABIN, MOONSTONE - CONTINUOUS 60 696 | Peter opens the door to the forepeak and sits the Shivering Soldier down on a narrow bunk. 697 | PETER 698 | I'll get you some more tea. 699 | Peter shuts the door. Looks at the bolt. Considering. 700 | 61 EXT. MOONSTONE - CONTINUOUS 61 701 | George looks up at the Commander. Addresses him with the tone of a child trying to speak like a grown up... 702 | GEORGE Is he a coward? 703 | Mr.Dawson looks sharply at George. 704 | MR.DAWSON 705 | He's shell-shocked, George. He's 706 | not himself. He may never be himself again. 707 | 62 INT. CABIN, MOONSTONE - MOMENTS LATER 62 708 | Peter hands the Shivering Soldier a cup of tea. The Shivering Soldier accepts it wordlessly. Staring in front of him. Peter closes the forepeak door. Pauses. 709 | Peter gently slides the bolt, LOCKING THE DOOR. 710 | CUT TO: 711 | 63 INT. COCKPIT, SPITFIRE 1 - DAY 63 712 | Farrier looks down at the mass of ships and boats passing each other- there is the minesweeper, CASTOR, every inch of her deck covered with troops- 713 | COLLINS (over radio) Heinkel, 11 0'clock, lining up to 714 | drop her load on that minesweeper- 715 | Farrier's head SNAPS around- spots the German bomber- FARRIER 716 | Fighters? 717 | 718 | 64 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 64 719 | Collins PEERS down- scanning around the Heinkel BOMBER for its fighter escort... SPOTS- 720 | COLLINS 109's- off her starboard- 721 | FARRIER (over radio) I'm on the bomber. 722 | Collins pushes forward into a DIVE... 723 | 65 EXT. SKY OVER ENGLISH CHANNEL - CONTINUOUS 65 724 | Spitfire 2 DIVES at the German fighters, cannons BLASTING... 725 | Spitfire 1 DIVES at the German bomber, cannons BLASTING... 726 | 66 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 66 727 | Farrier has the Heinkel in his sights, bucking and weaving 728 | as his spitfire SLICES down through turbulent air... he pushes the button on the stick which control his guns... 729 | He ROLLS away from the Heinkel as he dives beneath it, taking his finger off the trigger, fighting the g's with his neck as he pulls out of the dive... 730 | 67 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 67 731 | Collins fires at one of the 109's until he sees SMOKE trailing- he DIVES between the German planes... 732 | 68 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 68 733 | Farrier SCANS his surroundings as he tries to orient himself relative to the Heinkel... 734 | Finding it, he pulls the stick, lining up for another run at it, this time from below... 735 | The bomber is in his sights- he fires his guns... 736 | He flashes past- dangerously close to its top turret which HURLS TRACER BULLETS at him- he sees SPARKING on the hull of the bomber- 737 | 69 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 69 738 | Coming around- starting to climb- Collins sees the Heinkel VEER OFF COURSE, heading away from the minesweeper- 739 | COLLINS 740 | She's turning- you must've damaged 741 | her. 742 | (CONTINUED) 743 | 26. 744 | 745 | 27. 746 | 69 CONTINUED: 69 747 | FARRIER (over radio) Where's the escort? 748 | COLLINS I got one of- 749 | BLAMBLAMBLAM!!! Cannon fire RIPS into Spitfire 2- Collins YANKS the stick but it's too late... flames leap from the fuselage... 750 | COLLINS (CONT'D) I'm going down. 751 | FARRIER (over radio) I'm on him- bail out. 752 | Collins checks his parachute, opens the canopy, the wind HOWLS inside the cockpit- he surveys the water below- 753 | SLIDES his canopy shut again. 754 | COLLINS 755 | The swell looks good, I'm ditching. 756 | CUT TO: 757 | 70 INT. HOLD, DESTROYER - NIGHT 70 758 | The MUNCHING and SLURPING of starving soldiers. 759 | The ENGINES kick into gear as the destroyer starts to move- A CHEER goes up around the hundreds of men in the hold... 760 | 71 EXT. DESTROYER - CONTINUOUS 71 761 | Up on deck, Gibson watches several ROW BOATS heading towards them- hearing the engines, they starts SHOUTING- 762 | MALE VOICES Wait! Wait for us! 763 | Gibson spots white water on the black sea- a wake- MALE VOICE (O.S.) 764 | TORPEDO! 765 | An EXPLOSION LIFTS WATER at the side of the ship- 766 | 72 INT. HOLD, DESTROYER - CONTINUOUS 72 767 | The cheering STOPS- BOOOOOMS SHUDDER the suddenly FRAGILE iron walls of the hold- MASSIVE PERCUSSIONS OF WOBBLING METAL SHEETS- 768 | 769 | 73 EXT. DESTROYER - CONTINUOUS 73 A BLAST THAT MOVES EVERY BOLT OF THE DESTROYER- 770 | 74 INT. HOLD, DESTROYER - CONTINUOUS 74 ANYONE STANDING IS THROWN OFF THEIR FEET- 771 | 75 EXT. DESTROYER - CONTINUOUS 75 772 | A VAST PLUME OF FIRE EXPLODES UP AND OUT OF THE FUNNEL- THE DECK BLASTS APART- 773 | 76 INT. HOLD, DESTROYER - CONTINUOUS 76 774 | MEN SCREAM AS THE IRON PLATES OF THE WALLS BUCKLE- A GLIMPSE OF WATER BLASTING IN- 775 | THE LIGHTS GO OUT- COMPLETE DARKNESS... 776 | SOUND OF MEN SCREAMING BARELY AUDIBLE OVER THE SOUND OF 777 | BLASTING WATER AND BENDING METAL- 778 | 77 EXT. DESTROYER - CONTINUOUS 77 779 | THE SHIP LISTS- RAPIDLY SINKING- 780 | THE ROW BOATS PULL AWAY, HARD- 781 | GIBSON PREPARES TO JUMP- 782 | GLANCES BACK AT THE CLOSED DOOR TO THE HOLD- JUMPS BACK- OPENS THE DOOR- 783 | 78 INT. HOLD, DESTROYER - CONTINUOUS 78 BLACKNESS- 784 | THE DIM LIGHT OF THE OPEN DOOR BECOMES A BEACON- TOMMY SPOTS GIBSON WAVING- 785 | TOMMY AND ALEX CLAW THEIR WAY UP THE STEPS AS THE ENTIRE SHIP GOES UNDER- 786 | 79 EXT. DESTROYER 787 | TOMMY AND ALEX THE WAVES AND- 788 | - CONTINUOUS 79 BURST FREE OF THE DOOR AS IT SINKS BENEATH 789 | FROM THE DISAPPEARING SHIP WITH THE STRENGTH 790 | CUT TO: 791 | THEY PULL AWAY 792 | BORN OF ABSOLUTE DESPERATION... 793 | 28. 794 | 795 | 80 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 80 George hears planes behind them- he looks up- 796 | THREE SPITFIRES IN CONFIDENT FORMATION SWEEP OVERHEAD... Mr.Dawson keeps his eyes on the black smoke ahead of them. 797 | MR.DAWSON 798 | Spitfires, George. Greatest plane 799 | ever built. 800 | George smiles. Then looks quizzical- 801 | GEORGE You didn't even look. 802 | MR.DAWSON 803 | Rolls Royce Merlin engines. Sweetest 804 | sound you could hear out here. 805 | 81 INT. CABIN, MOONSTONE - CONTINUOUS 81 806 | Peter is folding a chart. A clicking sound catches his attention... the handle of the forepeak door is being rattled from the other side. Peter FREEZES, uncertain what to do... 807 | BANG- 808 | Peter BANG! 809 | Peter 810 | 82 EXT. MOONSTONE - CONTINUOUS 82 811 | Peter pokes his head out- Mr.Dawson looks at him, quizzical- 812 | PETER 813 | He wants to come out- 814 | The banging and SHOUTING of the Shivering Soldier continues- 815 | MR.DAWSON 816 | What did you do? Lock him in? 817 | Peter is at a loss- 818 | MR.DAWSON (CONT'D) Let him out, for God's sake! 819 | the rattles become BANGS- 820 | SHIVERING SOLDIER (O.S.) Hello?! Anyone there?! 821 | puts the chart down, takes a step towards the door- 822 | SHIVERING SOLDIER (O.S.) (CONT'D) OPEN UP, DAMMIT! 823 | freezes. Turns back to the companionway- 824 | 29. 825 | 826 | 83 INT. CABIN, MOONSTONE - CONTINUOUS 83 827 | Peter comes down the companionway, reluctantly approaching the BANGING, RATTLING DOOR... 828 | THE BANGING STOPS... 829 | Peter reaches up to the bolt, BRACES, gently SLIDES it back... 830 | Opens the door- the forepeak is EMPTY... Peter rushes in, spots the open forward hatch... 831 | 84 EXT. MOONSTONE, CONTINUOUS 84 Mr.Dawson leans down to try and see in the cabin- 832 | SHIVERING SOLDIER (O.S.) You haven't turned around! 833 | Mr.Dawson turns calmly to the Shivering Soldier. 834 | MR.DAWSON No. We have a job to do. 835 | SHIVERING SOLDIER Job? This is a pleasure yacht! 836 | You're weekend sailors, not the bloody navy-! A man your age- 837 | MR.DAWSON 838 | Men my age dictate this war. Why 839 | are we allowed to send our children to fight it? 840 | SHIVERING SOLDIER YOU SHOULD BE AT HOME! 841 | MR.DAWSON 842 | There won't be any home if we allow 843 | this slaughter across the channel. There's no hiding from this. 844 | CUT TO: 845 | 85 INT. COCKPIT, SPITFIRE 1 - DAY 85 846 | Farrier chases the 109 as it circles around on Collins... 847 | 86 INT. COCKPIT, SPITFIRE 2 - DAY 86 848 | Collins glances out at his burning wing- 849 | Checks his altimeter- 850 | Checks his canopy is locked in the half-open position- LOWER... 851 | 30. 852 | 853 | 87 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 87 Farrier fires at the 109, chasing him off- 854 | FARRIER 855 | He's turned tail, I'm after him- 856 | 88 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 88 Collins checks his belts are tight- 857 | Checks the release pin on his harness- 858 | COLLINS 859 | Good luck. Watch your fuel... 860 | (reads) Fifteen gallons. 861 | Checks his Mae West, puffing into the inflating tube- 862 | LOWER... 863 | 89 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 89 Farrier grease pencils the reading on the chart- 864 | FARRIER 865 | Fifteen gallons, understood... 866 | 90 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 90 867 | FARRIER (over radio) ...best of luck, Collins. 868 | Collins checks wind direction- 869 | Checks wave direction on the surface of the water- LOWER... 870 | Turns, lining up along the the waves as he descends... LOWER... 871 | The water rushes by BLINDINGLY FAST... 872 | 91 INT. COCKPIT, SPITFIRE 1 - DAY 91 873 | Farrier watches Spitfire 2 CARVE gracefully across the water, before coming to a stop, floating. 874 | Farrier spots a CIVILIAN YACHT heading for Collins... 875 | (CONTINUED) 876 | 31. 877 | 878 | 32. 879 | 91 CONTINUED: 91 880 | He sees Collins' hand stick out of the canopy, WAVING... He TIPS HIS WINGS at Collins- turns away, looks ahead, chasing the 109 towards Dunkirk... 881 | CUT TO: 882 | 92 EXT. WATER, JUST OUTSIDE DUNKIRK HARBOUR - NIGHT 92 883 | Tommy and Alex, life jackets on, swim on the swell, bodies and burning wreckage all around. Fuel BURNING on the surface of the water. 884 | Tommy and Alex pull for an OVERLOADED row boat. Tommy GRABS the side, tries to climb- he's PUSHED OFF by the men inside- 885 | MALE VOICE Piss off- it's too crowded! 886 | Alex is GRABBING at the rail as well- ALEX 887 | You can't leave us! 888 | SOLDIER You men, leave off. the boat- it's gone the way out here... 889 | Make some room- 890 | (O.S.) 891 | You'll capsize 892 | over twice on 893 | Tommy looks at the Soldier. It is the Shivering Soldier, NOT YET SHIVERING. IN FULL CONTROL OF HIS FACULTIES. 894 | SOLDIER (CONT'D) 895 | You have to stay calm. There are 896 | plenty of boats. 897 | ALEX 898 | Calm?! Wait till you get torpedoed, 899 | then tell us to be calm! 900 | SOLDIER You have life jackets? 901 | MALE VOICE Yeah, they do. 902 | SOLDIER 903 | Don't panic, the water's not too 904 | rough, or too cold. We're heading back to the beach- 905 | MALE VOICE 906 | Fuck off! Let's go to Dover! 907 | Several voices join in. 908 | (CONTINUED) 909 | 910 | 33. 911 | 92 CONTINUED: 92 912 | SOLDIER 913 | We can't make it across the channel 914 | on this, lads. We need to get back to the beach and wait for another ride. 915 | (gestures) 916 | It's not even half a mile. You men in the water float here, save your strength, we'll come back for you. 917 | The men start rowing. 918 | Gibson is in the back. Alex SPOTS him. 919 | Gibson quietly drops the rear painter (a small rope attached to the stern) into the black water. 920 | Alex takes it, hands part of it to Tommy and they quietly drag behind the boat as it rows in to the shore... the men in the rear notice, but nobody says anything... 921 | As the dawn breaks, the small, packed boat pulls across the calm water to the vast, packed beach at Dunkirk. 922 | 93 EXT. MOONSTONE - DAY 923 | The Shivering Soldier steps up to Mr.Dawson- 924 | SHIVERING SOLDIER 925 | What is it you think you can do out 926 | there?! On this thing?! 927 | MR.DAWSON 928 | Not just us. The call went out- we 929 | won't be the only ones to answer. 930 | SHIVERING SOLDIER YOU DON'T EVEN HAVE GUNS! 931 | MR.DAWSON Did you have a gun? 932 | SHIVERING SOLDIER Course. A rifle. 303. 933 | MR.DAWSON 934 | Did it help you against the dive 935 | bombers? Or the U-boats? 936 | The Shivering Soldier GLARES at Mr.Dawson. 937 | SHIVERING SOLDIER 938 | You're an old fool. And you're going 939 | to die if you don't turn around. (CONTINUED) 940 | 93 941 | CUT TO: 942 | 943 | 34. 944 | 93 CONTINUED: 93 945 | The BOOMS echo. Closer now. 946 | SHIVERING SOLDIER (CONT'D) We're turning around, now! 947 | The Shivering Soldier steps towards Mr.Dawson. SCREAMING at the top of his lungs- 948 | SHIVERING SOLDIER (CONT'D) TURN IT AROUND! TURN IT AROUND!- 949 | Peter, hearing this, makes his way back from the bow- 950 | The Shivering Soldier GRABS the wheel- 951 | George GRABS his shoulder- 952 | The Shivering Soldier SMASHES HIS ELBOW into George's face, sending him flying BACKWARDS DOWN THE COMPANIONWAY- 953 | Peter PULLS the Shivering Soldier away from the wheel. 954 | PETER Calm it down, Mate. 955 | The Shivering Soldier looks at him, shocked. Confused. Peter calls down the companionway- 956 | Nothing. 957 | George? 958 | George?! 959 | PETER (CONT'D) 960 | PETER (CONT'D) 961 | Nothing. The Shivering Soldier watches as Peter climbs down to find- 962 | 94 INT. CABIN, MOONSTONE - CONTINUOUS 94 963 | George, sprawled out at the foot of the steps, on his back, QUIETLY GROANING, BLEEDING from the back of the head. Peter grabs a life jacket and puts it behind George's head. 964 | PETER 965 | It's okay. You're okay. It's okay. 966 | George blinks at Peter. Frightened. 967 | 95 INT. COCKPIT, SPITFIRE 1 - DAY 968 | Farrier chases the 109, gradually CLOSING... 969 | CUT TO: 970 | (CONTINUED) 971 | 95 972 | 973 | 35. 974 | 95 CONTINUED: 95 975 | Up ahead- a convoy of ships is gathered around the entrance to the harbour... 976 | Farrier passes over a FISHING TRAWLER WITH A BLUE CABIN, covered with soldiers, strangely low in the water, WATER WASHING ACROSS ITS DECKS... 977 | He looks up ahead to the 109, just coming into range... 978 | He spots GERMAN PLANES in the distance, heading towards him... he sights the 109... FIRES a short burst... NOTHING... 979 | HE REMEMBERS HIS FUEL GAUGE... 980 | -pointlessly pushes the button next to the cracked gauge. 981 | NO RESPONSE. 982 | Farrier checks his position on his chart. Checks the last fuel reading he grease-penciled... KNOWS HE SHOULD TURN AROUND- 983 | Farrier sights the 109, banking slightly to bring it across his sights... 984 | Farrier FIRES- the 109 starts SMOKING, DROPPING- 985 | Farrier SPINS AROUND- TURNING AWAY from the approaching planes, HEADING FOR DOVER... FOR HOME. 986 | As he passes over the sinking blue trawler, he sees men jumping into the water, swimming for a DESTROYER nearby... 987 | In his REAR VIEW MIRROR; the enemy planes approaching... Farrier looks at his cracked fuel gauge... THINKING... 988 | CUT TO: 989 | 96 EXT. BEACH AT ZYDECOTE (7 MILES EAST OF DUNKIRK) - DAWN 96 990 | The SURF has picked up since yesterday... 991 | Tommy, Gibson, Alex lie on the beach, sleeping as the light comes up on a stormy day. 992 | In the distance, towards the dark smoke of Dunkirk, the lines of men extend into the sea. 993 | Nearby, small groups of soldiers attempt to climb onto small vessels- rowboats are being SWAMPED and OVERTURNED in the surf, overcrowded boats are GROUNDED on the sand- 994 | MALE VOICE 995 | Right. Three of you out, or the 996 | rest's stuck. 997 | (CONTINUED) 998 | 999 | 36. 1000 | 96 CONTINUED: 96 1001 | Soldiers give up their places. Some head back out of the surf. Some wade out past the break... 1002 | 97 EXT. BEACH AT MALO LES BAINS - CONTINUOUS 97 1003 | Colonel Winnant walks the beach, surveying. He approaches a group of ENGINEERS driving TRUCKS onto the sand, taking the air out of their tires, laying duckboards on top... 1004 | ENGINEER (brightly) 1005 | A pier. When the water comes back in. Tide's turning, now. 1006 | Colonel Winnant looks out at the churning water. 1007 | COLONEL WINNANT How can you tell? 1008 | ENGINEER 1009 | (quietly) 1010 | The bodies come back. 1011 | Colonel Winnant looks out at the water- men in line, chest deep, gently push floating bodies aside as they wash in. 1012 | 98 EXT. BEACH AT ZYDECOTE - CONTINUOUS 98 1013 | Tommy BANGS a tin of vegetables on a rock. It springs a leak and he sucks the juice. Gibson holds out his hand. Tommy keeps sucking for a beat or two, then hands it over. 1014 | Tommy watches Vanquisher loading troops from the vast crowd lining the mole. Despairing. 1015 | Alex opens his eyes and sits up. Spots some HIGHLANDERS walking past, away from Dunkirk in loose formation... 1016 | ALEX Hey! Highlanders! 1017 | Tommy watches as Alex gets to his feet, heading over regimental comrades. 1018 | ALEX (CONT'D) What's that way? 1019 | HIGHLANDER1 1020 | (points) 1021 | A boat. 1022 | Alex follows his gesture to a FISHING TRAWLER WITH A CABIN, listing in the shallows a mile up the beach. 1023 | ALEX She's grounded. 1024 | to his 1025 | BLUE 1026 | (CONTINUED) 1027 | 1028 | 37. 1029 | 98 CONTINUED: 98 1030 | HIGHLANDER2 1031 | Not when the tide comes in, she isn't. 1032 | Tommy and Gibson are already on their feet. Alex nods at them as they follow the Highlanders down the beach towards the grounded trawler... 1033 | CUT TO: 1034 | 99 INT. CABIN, MOONSTONE - DAY 99 1035 | Peter goes down below to check on George. He checks the bleeding on the life jacket behind George's head. 1036 | PETER 1037 | What'd you want to come along for, 1038 | George? 1039 | GEORGE 1040 | Sea cadet? You and Mr.Dawson? Best 1041 | thing I ever done. Only thing I ever done. I told my dad I never done nothing at school. I told my dad I'd do something one day. Maybe get in the local paper. 1042 | PETER The Herald? Why? 1043 | GEORGE 1044 | Maybe teachers would see it. Make 1045 | my school proud. 1046 | PETER (laughs) 1047 | Who cares what your bloody school thinks, George? 1048 | George looks up at Peter, desperate. 1049 | GEORGE 1050 | Please! Please don't laugh at me! 1051 | Peter looks at George, deciding how to respond. 1052 | PETER 1053 | I'm going to laugh at you, George- 1054 | 'cos you're being bloody silly. George is crying. 1055 | PETER (CONT'D) 1056 | Now, stop it. I need you back up on 1057 | deck. George keeps crying. 1058 | (CONTINUED) 1059 | 1060 | 38. 1061 | 99 CONTINUED: 99 1062 | GEORGE 1063 | I can't. I can't see. 1064 | Peter looks at him. Gets a blanket, puts it across George's chest. 1065 | PETER Get some rest. 1066 | Peter gets up. Looks down at the softly weeping boy. 1067 | PETER (CONT'D) 1068 | I'll need you as soon as you're able. 1069 | George nods. Smiling through his tears. 1070 | 100 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 100 1071 | Peter comes up on deck. 1072 | The Shivering Soldier, crouched in the well, STARES at him. Mr.Dawson is at the helm. Peter comes close. Speaking low- 1073 | PETER 1074 | The blood won't stop. Should we 1075 | turn back? 1076 | Mr.Dawson looks back towards Britain. Then forward to France. Thinking. Shakes his head. 1077 | MR.DAWSON 1078 | Come too far. 1079 | BOOM! Explosions nearby- 1080 | The Shivering Soldier moves into a foetal position. 1081 | Mr.Dawson and Peter look ahead to where PLUMES of water rise, seemingly in slow motion, amongst the ships up ahead- 1082 | German BOMBERS drifting overhead, 109 fighters buzzing around them... 1083 | Mr.Dawson holds his course... 1084 | CUT TO: 1085 | 101 INT. COCKPIT, SPITFIRE 1 - DAY 101 1086 | Farrier flies, DISTRACTED, glancing from his broken fuel gauge to the switch for his reserve fuel tank... 1087 | FARRIER 1088 | Sod it. 1089 | Farrier BANKS, COMING AROUND... 1090 | (CONTINUED) 1091 | 1092 | 39. 1093 | 101 CONTINUED: 101 1094 | Farrier CLIMBS, trying to gain advantage for the coming encounter... lining up on the German planes threatening the Destroyer and the blue trawler... 1095 | CUT TO: 1096 | 102 EXT. BASE OF THE MOLE - CONTINUOUS 102 1097 | Colonel Winnant makes his way towards the crowded mole. Stretchers of FRENCH TROOPS are brought down to the mole. A PRIVATE comes out of the crowd, BREATHLESS. 1098 | PRIVATE 1099 | The French've been forced back on 1100 | the western side, sir. 1101 | Colonel Winnant looks at EXPLOSIONS over the warehouses. 1102 | COLONEL WINNANT 1103 | But they're still holding a perimeter? 1104 | PRIVATE 1105 | For now. 1106 | Colonel Winnant pushes on down the mole... 1107 | 103 EXT. THE MOLE - MOMENTS LATER 103 He finds Bolton, but NO SHIPS... 1108 | COLONEL WINNANT Where're the destroyers? 1109 | COMMANDER BOLTON There'll be one soon. 1110 | COLONEL WINNANT 1111 | One? 1112 | COMMANDER BOLTON After yesterday's losses, it's one 1113 | ship on the mole at a time. 1114 | COLONEL WINNANT 1115 | The battle's here, what're they saving 1116 | them for? 1117 | COMMANDER BOLTON 1118 | The next battle. The one for Britain. 1119 | Same with the planes. 1120 | COLONEL WINNANT (peers through his 1121 | field glasses) 1122 | But it's right there! You can practically- 1123 | (CONTINUED) 1124 | 1125 | 103 CONTINUED: 1126 | 103 1127 | COMMANDER BOLTON 1128 | Seeing home doesn't help us get there, 1129 | Captain. 1130 | Colonel Winnant turns to the flaming town at their backs... 1131 | COLONEL WINNANT 1132 | They need to send more ships, dammit! 1133 | Every hour the enemy pushes closer. 1134 | COMMANDER BOLTON They've activated the small vessels 1135 | pool- 1136 | COLONEL WINNANT Vessels pool? 1137 | COMMANDER BOLTON The list of civilian boats for 1138 | requisition- 1139 | COLONEL WINNANT Civilian? We need destroyers. 1140 | COMMANDER BOLTON 1141 | Small boats could load from the beach. 1142 | Colonel Winnant watches men struggling to load in the surf... 1143 | COLONEL WINNANT Not in these conditions. 1144 | COMMANDER BOLTON I'd rather face waves than dive 1145 | bombers. 1146 | Colonel Winnant looks up at the cloudy sky- 1147 | COLONEL WINNANT You're right- they won't get up in 1148 | this... 1149 | COLONEL WINNANT (CONT'D) 1150 | (points) 1151 | The Royal Engineers are building piers from lorries- should help when the tide comes back. 1152 | COMMANDER BOLTON We'll know in six hours. 1153 | COLONEL WINNANT 1154 | I thought tides were every three? 1155 | (CONTINUED) 1156 | 40. 1157 | 1158 | 41. 1159 | 103 CONTINUED: (2) 103 1160 | COMMANDER BOLTON 1161 | Then it's good that you're army and 1162 | I'm navy, isn't it? 1163 | Colonel Winnant allows himself a smile. Commander Bolton spots a shape on the horizon. 1164 | COMMANDER BOLTON (CONT'D) Vanquisher... 1165 | CUT TO: 1166 | 104 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 104 1167 | Mr.Dawson, at the helm, studies the horizon. Peter joins him, GLARING at the Shivering Soldier before taking a seat. 1168 | Mr.Dawson hears something, starts scanning the sky... Spots a distant plane... Peter follows his gaze... 1169 | Mr.Dawson THROWS the wheel, bearing to starboard, hard, throttling up- 1170 | MR.DAWSON 1171 | Heinkel. 1172 | Mr.Dawson points at a MINESWEEPER heading towards them... 1173 | MR.DAWSON (CONT'D) They'll go for the minesweeper. 1174 | PETER 1175 | Shouldn't we stand by? To pick up 1176 | survivors? 1177 | MR.DAWSON 1178 | To do that we have to survive 1179 | ourselves. 1180 | As the boat MOTORS away, Peter looks back to see the HEINKEL AND ITS TWO FIGHTERS moving towards the MINESWEEPER... 1181 | CUT TO: 1182 | 105 EXT. GROUNDED TRAWLER - DAY 105 1183 | The Highlanders approach, cautiously... the beach is DESERTED here- just disabled army vehicles and dead bodies... 1184 | The blue trawler is tilted towards them... they circle the hull, checking it... it seems sound enough. 1185 | Tommy and Gibson follow the Highlanders as they climb up onto the abandoned trawler... 1186 | 1187 | 106 EXT. DECK OF GROUNDED TRAWLER - CONTINUOUS 106 1188 | Tommy looks over at the dunes above them. Alex looks around the boat- turns to Highlander1 1189 | ALEX Where's the crew? 1190 | HIGHLANDER1 1191 | Probably got spooked after they ran 1192 | aground. Scarpered up the beach. ALEX 1193 | Why? 1194 | HIGHLANDER2 1195 | We're outside the perimeter. Enemy 1196 | could be right there- Points at the dunes. 1197 | HIGHLANDER2 (CONT'D) Best shut ourselves inside and wait 1198 | for high tide... 1199 | Highlander3 heads down the companionway into the small hold. 1200 | ALEX How long's that? 1201 | HIGHLANDER3 Every three hours. 1202 | They descend into the hold, shutting the door behind them. CUT TO: 1203 | 107 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 107 1204 | As Moonstone ploughs through the swell, Peter looks back at the Heinkel coming over the minesweeper... 1205 | Peter SPOTS- 1206 | PETER Spitfires! Dad, Spitfires! 1207 | Mr.Dawson TURNS to see two Spitfires DIVING at the German bomber and its fighter escort- 1208 | One Spitfire DIVES RIGHT BETWEEN TWO 109'S SETTING ONE ALIGHT- 1209 | PETER (CONT'D) He got him, he got him!! 1210 | The other Spitfire flies close over the Heinkel, which turns away from the ship- Mr.Dawson eases back on the speed... 1211 | (CONTINUED) 1212 | 42. 1213 | 1214 | 43. 1215 | 107 CONTINUED: 107 1216 | MR.DAWSON 1217 | The Heinkel's moved off... 1218 | As they watch, ONE OF THE SPITFIRES STARTS SMOKING... PETER 1219 | Oh, no. 1220 | Mr.Dawson sees the smoke- THROWS the wheel, spinning the yacht around to head back- 1221 | MR.DAWSON Watch for a parachute! 1222 | Mr.Dawson THROTTLES up... 1223 | CUT TO: 1224 | 108 INT. COCKPIT, SPITFIRE 1 - DAY 108 1225 | Farrier hears his engine skip a beat- puts his gloved finger on the RESERVE TANK TOGGLE SWITCH, LISTENING... his engine evens out again. He puts his hand back on the stick, focusing on the German planes... 1226 | He THROTTLES UP, speeding into the fray, CLIMBING... 1227 | CUT TO: 1228 | 109 INT. HOLD, GROUNDED TRAWLER - DAY 109 1229 | Dimly lit by a couple of small, dirty portholes. 1230 | The soldiers lie around the hold. Sleeping or chatting. Alex is scrounging around the hold, finding nothing useful. 1231 | ALEX (to Gibson) 1232 | Poke your head out, see if the water's coming in. 1233 | Gibson shakes his head, pulling his arms tight around himself. Alex GLARES at him- 1234 | ALEX (CONT'D) Talkative sod. 1235 | Tommy gets up, climbs up to the door, cracks it- CRAWLS up into the well- PEEKS over the rail- 1236 | The boat is in INCHES OF WATER. 1237 | TOMMY 1238 | Bugger. Barely come in at all. 1239 | ALEX For fuck's sake. 1240 | (CONTINUED) 1241 | 1242 | 44. 1243 | 109 CONTINUED: 109 1244 | HIGHLANDER3 1245 | Calm down. What goes out comes back 1246 | in, right? 1247 | ALEX Yeah, but how long? 1248 | Silence answers this. Clearly no sailors aboard. 1249 | CUT TO: 1250 | 110 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 110 1251 | The Moonstone PUSHES through the swell, FULL SPEED, diesel engine STRAINING... 1252 | Peter watches the SMOKE-TRAILING SPITFIRE fly LOWER and LOWER... 1253 | PETER No parachute... 1254 | Mr.Dawson is watching the plane like a hawk, steering around the waves by instinct... 1255 | 111 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 111 The water FLASHES BY BLINDINGLY FAST... 1256 | Collins PULLS BACK ON THE STICK, raising the nose as the plane- 1257 | HITS THE WATER WITH A JOLT AND A TEARING SOUND- 1258 | COLLINS THRASHED AGAINST HIS BELTS FORWARD/BACK/LEFT/RIGHT- BANG- 1259 | 112 EXT. MOONSTONE- CONTINUOUS 112 Peter watches the Spitfire 'land' on the surface of the water- 1260 | PETER 1261 | He's down. 1262 | 113 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 113 1263 | With a SHOOOOOSH, the plane is floating over the swell- 1264 | Like a sprinter hearing the gun- Collins RELEASES HIS BELTS, starts INFLATING his life vest- pulls the catch on the canopy, YANKING it back along its track- it JAMS... he THRUSTS his hand through the gap, STRUGGLING... From outside it looks like he is WAVING... 1265 | He looks up to see Farrier's spitfire shoot over, DIPPING A WING IN SALUTE... 1266 | (CONTINUED) 1267 | 1268 | 45. 1269 | 113 CONTINUED: 113 1270 | Collins sits in the gently bobbing plane, collecting himself as he watches the water start to rise around the slowly SINKING PLANE... 1271 | Collins tries the canopy again- JAMMED. 1272 | HE IS TRAPPED IN THE SINKING PLANE... 1273 | CUT TO: 1274 | 114 INT. COCKPIT, SPITFIRE 1 - DAY 114 1275 | Farrier levels off, looking down at the Heinkel approaching- It has a SINGLE FIGHTER ESCORT- an ME 109 off the port wing... 1276 | CUT TO: 1277 | 115 INT. HOLD, GROUNDED TRAWLER - DAY 115 1278 | Tommy JOLTS awake- there are STEPS outside- he moves up to the door. Highlander1 gets his rifle, moves in front of the door. Aims. Nods at Tommy... 1279 | Tommy throws open the door- a SEAMAN stands there- SEAMAN 1280 | Nee, nee! Highlander1 is confused. 1281 | Tommy GRABS the Seaman, PULLING him down into the hold. Highlander1 holds his gun on him- 1282 | ALEX 1283 | Kraut? 1284 | The Seaman looks uncomprehendingly up at Alex- 1285 | ALEX (CONT'D) Are you German?! 1286 | SEAMAN 1287 | Dutch! Dutch! Merchant navy. Here 1288 | to pick you up. To help you. They sit him up. 1289 | ALEX 1290 | Why'd you leave your boat? 1291 | SEAMAN 1292 | In case Germans come. We wait up 1293 | the beach with the soldiers. Wait for the tide. 1294 | (CONTINUED) 1295 | 1296 | 46. 1297 | 115 CONTINUED: 115 1298 | HIGHLANDER2 1299 | You came back, the tide must be in. 1300 | SEAMAN 1301 | Coming, yes. But more hours till we 1302 | float. 1303 | ALEX 1304 | Hours?! Why'd you come back? 1305 | The Seaman gestures around the packed hold- 1306 | SEAMAN 1307 | Not so heavy when I left! 1308 | Alex and the others take this in. 1309 | A GUNSHOT PENETRATES THE HULL- everybody LIES FLAT, Tommy STARES at the bullet hole, which lets in light... 1310 | CUT TO: 1311 | 116 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 116 1312 | Mr.Dawson PUSHES the boat towards where the plane went down. 1313 | PETER 1314 | There was no 'chute, dad... 1315 | Mr.Dawson ignores him. The engine is SCREAMING... 1316 | PETER (CONT'D) Dad, there was no 'chute. He's 1317 | probably dead- MR.DAWSON 1318 | (snaps) 1319 | Damn it, he might be alive!! 1320 | Peter is SHOCKED at his dad's outburst. Mr.Dawson stares at where the plane went down... 1321 | CUT TO: 1322 | 117 INT. COCKPIT, SPITFIRE 1 - DAY 117 1323 | Farrier lines up for his attack... sighting the Heinkel as it commits to its bombing run over the Destroyer- 1324 | Farrier pushes forward on his stick, going into his DIVE... CUT TO: 1325 | 118 INT. HOLD, GROUNDED TRAWLER - DAY 118 Everybody STARES at the bullet hole, not making a sound... 1326 | (CONTINUED) 1327 | 1328 | 47. 1329 | 118 CONTINUED: 118 1330 | Another SHOT PUNCHES A HOLE 2 FEET FROM THE FIRST- 1331 | Highlanders near the holes ease away, SQUEEZING up against other soldiers... 1332 | BANG! A third shot, directly above the first... 1333 | Two Highlanders GRAB their rifles, going for the stairs- 1334 | TOMMY 1335 | No! Then they'll know we're in here. 1336 | HIGHLANDER1 1337 | Why else are they shooting at us?! 1338 | TOMMY 1339 | Look at the grouping... 1340 | Everybody looks at the three bullet holes. TOMMY (CONT'D) 1341 | ...target practice. 1342 | BANG! A fourth hole, near the others... 1343 | CUT TO: 1344 | 119 EXT. MOONSTONE, ENGLISH CHANNEL - DAY 119 1345 | The Moonstone is getting closer to the Spitfire bobbing on the waves. 1346 | Close enough to see that it is SINKING... 1347 | MR.DAWSON 1348 | Peter, go forward with the boat hook. 1349 | 120 INT. COCKPIT, SPITFIRE 2 - CONTINUOUS 120 1350 | Collins SMASHES the canopy back and forth on its track... JAMMED, JAMMED, JAMMED. 1351 | WATER STARTS POURING IN, streaming through the gap in the half-open canopy... he SHUTS IT... TRAPPED... OPENS IT, YANKING, WATER POURING IN... 1352 | Collins SEARCHES around looking for inspiration, for an implement, for ANYTHING- 1353 | Water RISING past his ankles... his calves... 1354 | 121 INT. COCKPIT, SPITFIRE 1 - DAY 1355 | Farrier DIVES, plummeting towards the Heinkel... 1356 | CUT TO: 1357 | (CONTINUED) 1358 | 121 1359 | 1360 | 48. 1361 | 121 CONTINUED: 121 1362 | He GLANCES across at the 109, which suddenly BANKS TOWARDS HIM, clearly REACTING to Farrier's ATTACK... 1363 | CUT TO: 1364 | 122 INT. HOLD, GROUNDED TRAWLER - DAY 122 1365 | As the men STARE at the bullet holes, WATER starts SLOPPING through the lowest ones... a highlander goes to plug the holes- BANG! The highlander SCREAMS, clutching his face- his comrades pull him back, trying to SMOTHER his cries... 1366 | The water pours in STEADILY through the lowest holes. Alex points at the target zone- 1367 | ALEX We have to plug it! 1368 | HIGHLANDER2 After you, mate! 1369 | They stay back from the holes, wary. Watching the WATER POUR IN... 1370 | CUT TO: 1371 | 123 INT. COCKPIT, SPITFIRE 2 - DAY 123 1372 | Collins pulls the steel FLARE GUN from its holder- 1373 | Water is coming up OVER HIS LEGS NOW... 1374 | He SMASHES the flare gun into the canopy, again and again... 1375 | CUT TO: 1376 | 124 INT. COCKPIT, SPITFIRE 1 - DAY 124 1377 | Farrier FIRES at the Heinkel- TRACERS ZIPPING at the bomber- The 109 RISES at him, GUNS BLAZING- 1378 | Farrier ROLLS away, trying to dodge the fire- 1379 | 125 EXT. SKY ABOVE DUNKIRK HARBOUR - DAY 125 1380 | As Spitfire 1 rolls away, the Heinkel releases its load- BOMBS FALLING AROUND THE DESTROYER- 1381 | CUT TO: 1382 | 126 INT. HOLD, GROUNDED TRAWLER - DAY 126 1383 | A BURST OF MACHINE GUN FIRE OPENS A NEW GROUP OF HOLES BESIDE THE FIRST- 1384 | (CONTINUED) 1385 | 1386 | 126 CONTINUED: 1387 | 126 1388 | Alex watches the water spraying in. He TURNS to the Dutch Seaman- 1389 | ALEX 1390 | How do we get off?! Do we need to 1391 | ditch some ballast?! 1392 | The Dutch Seaman looks at him, uncomprehending- 1393 | ALEX (CONT'D) 1394 | Weight! Do we need to lose weight! 1395 | The Dutch Seaman shrugs- 1396 | SEAMAN Weight, yes. 1397 | Alex turns to face the group- 1398 | ALEX 1399 | Somebody needs to get off. 1400 | HIGHLANDER1 Well volunteered. 1401 | ALEX 1402 | We don't need a volunteer. I know 1403 | someone who ought to get off... 1404 | Alex turns to Tommy and Gibson. Points at Gibson. 1405 | ALEX (CONT'D) This one. He's a German spy. 1406 | TOMMY Don't be daft. 1407 | Alex stares Gibson down... 1408 | ALEX 1409 | He's a bloody jerry. You might not've 1410 | noticed that he hasn't said a word, but I have. He doesn't speak English- or if he does it's with an accent thicker than sauerkraut sauce- 1411 | TOMMY You're daft. Tell him. 1412 | Gibson just stares at Alex... 1413 | ALEX Yeah, tell me. 1414 | (CONTINUED) 1415 | 49. 1416 | 1417 | 50. 1418 | 126 CONTINUED: (2) 126 1419 | Nothing. Just the sound of water SPRAYING IN HARD- jetting in through the bottom holes... 1420 | 127 INT. COCKPIT, SPITFIRE 2 - Collins SMACKS the canopy- The flare gun BOUNCES OFF- He DROPS the flare gun- SCRAMBLES to find it under The water is RISING UP HIS 1421 | DAY 1422 | the water- 1423 | CHEST... 1424 | 127 1425 | 128 INT. COCKPIT, SPITFIRE 1 - 1426 | Farrier CUTS RIGHT, dodging away from 1427 | BANKING HARD, he gets a clear look at the EXPLOSIONS- 1428 | CUT TO: 1429 | the 109- 1430 | the Destroyer weathering 1431 | 128 1432 | A PLUME OF WATER RIGHT NEXT he flies through the top of 1433 | 129 INT. HOLD, GROUNDED TRAWLER 1434 | Alex TURNS to Highlander 1- Highlander 1 hands it over- 1435 | TO THE DESTROYER comes so high its spray- 1436 | DAY 1437 | CUT TO: 1438 | - DAY 129 1439 | holds out his hand for his gun. 1440 | Alex moves at Gibson pointing the rifle, he hooks the barrel on Gibson's tags, pulling them closer to read- 1441 | ALEX Tell me..., Gibson! 1442 | Tommy looks at Gibson, panicking- 1443 | TOMMY 1444 | Tell him, for God's sake! 1445 | Alex PUSHES the rifle against Gibson's CHEEK- Gibson CRACKS- 1446 | GIBSON FRANCAIS! JE SUIS FRANCAIS! 1447 | Tommy stares, SHOCKED. Alex moves back slightly, taking this in... 1448 | A BURST OF MACHINE GUN FIRE- everyone DUCKS from ricochets. 1449 | (CONTINUED) 1450 | CUT TO: 1451 | 1452 | 129 CONTINUED: 1453 | 129 1454 | ALEX 1455 | A Frog. A bloody Frog. A cowardly 1456 | little queue-jumping frog... 1457 | With the end of his rifle Alex shakes Gibson's tags- 1458 | ALEX (CONT'D) Who's Gibson, eh? A naked dead 1459 | Englishman lying out on that sand. Or did you at least have the decency to bury him? 1460 | 'Gibson' just stares. 1461 | TOMMY 1462 | He did. I helped him- I thought it 1463 | was his mate. 1464 | ALEX Maybe he killed him- 1465 | TOMMY He didn't kill him- 1466 | ALEX How do we know?! 1467 | TOMMY 1468 | How hard is it to find a dead 1469 | Englishman on Dunkirk beach, for God's sake?! He didn't kill anyone- he was looking for a way off the damned sand like the rest of us! 1470 | The water is SPRAYING IN FROM MORE AND MORE HOLES as the WATER LEVEL RISES... 1471 | Alex has the rifle on Gibson. Another BURST OF MACHINE GUN FIRE- 1472 | HIGHLANDER2 1473 | Haven't they had enough practice by 1474 | now?!!! 1475 | HIGHLANDER1 1476 | They're making sure she won't float. 1477 | Highlander2 looks at the holes SPRAYING WATER, the water POOLING in the bottom of the hold- he turns to the Seaman- 1478 | HIGHLANDER2 Will she still float?! 1479 | The Seaman assesses the leaks... 1480 | (CONTINUED) 1481 | 51. 1482 | 1483 | 129 CONTINUED: (2) 1484 | 129 1485 | SEAMAN 1486 | Float, yes. With less weight, yes- 1487 | ALEX 1488 | And we know who's getting off- 1489 | TOMMY 1490 | You can't do that. We're on the 1491 | same side. 1492 | Alex NUDGES Gibson with the rifle- 1493 | ALEX Go on- up you go- 1494 | TOMMY 1495 | As soon as he pokes his head out 1496 | they'll slaughter him- 1497 | ALEX Better him than me- 1498 | TOMMY It's not fair- 1499 | ALEX Survival's not fair. 1500 | HIGHLANDER1 1501 | No, it's shit. It's fear and greed. 1502 | Fate squeezed through the bowels of men. Shit. 1503 | TOMMY He saved our lives. 1504 | HIGHLANDER2 1505 | And he's about to do it again- go on- 1506 | Alex starts shoving Gibson up the stairs- 1507 | TOMMY No! Just stop! 1508 | Alex turns to Tommy- looks him in the eye- 1509 | ALEX 1510 | We need someone to get off so the 1511 | rest of us can live- you want to volunteer? 1512 | TOMMY 1513 | Fuck no. I'm going home. 1514 | ALEX 1515 | And if this is the price? 1516 | (CONTINUED) 1517 | 52. 1518 | 1519 | 53. 1520 | 129 CONTINUED: (3) 129 1521 | TOMMY 1522 | I'll live with it, but it's wrong. 1523 | Alex shoves Gibson up another step, opens the door- 1524 | TOMMY (CONT'D) 1525 | Alex, one man's not going to make 1526 | enough difference- 1527 | HIGHLANDER1 1528 | You'd best hope it does- 'cos you'd 1529 | be volunteering next- TOMMY 1530 | What? 1531 | ALEX (indicates Highlanders) 1532 | We're regimental brothers, mate. Just the way it is. 1533 | Gibson GRABS for the rifle- Tommy JUMPS at Alex to help Gibson- they SMASH against the hull- as they drop into the water, 1534 | the ship LEVELS- 1535 | SEAMAN FLOAT! WE FLOAT! 1536 | HIGHLANDER2 START THE BLOODY ENGINE-! 1537 | The Seaman is already crawling out the hatch, reaching up to- The ENGINE STARTS- LOUD as LOUD can be- 1538 | MACHINE GUN FIRE STRAFES THE HULL- THE MEN DUCK BELOW THE WATERLINE... 1539 | The Seaman THROWS the screw into reverse, FULL THROTTLE... 1540 | The men HOLD THEIR BREATHS UNDER THE WATER AT THE BOTTOM OF THE HOLD AS BULLETS PEPPER THE HULL... 1541 | CUT TO: 1542 | 130 INT. COCKPIT, SPITFIRE 2 - DAY 130 1543 | Collins, WATER UP TO HIS EARS NOW, grabs the flare gun- SWINGING UNDERWATER LESS EFFECTIVE- 1544 | Now he PANICS, pushing his face up against the canopy, BANGING WITH HIS FISTS- INSTINCT TAKING OVER- NO MORE THOUGHT, NO MORE PLAN- BANGING, BANGING- WATER RISING OVER HIS EARS- 1545 | SMASH- SOMETHING CRACKS INTO THE CANOPY RIGHT ABOVE HIS HEAD- he RECOILS- it IMPACTS the canopy again- SMASHING A HOLE- 1546 | (CONTINUED) 1547 | 1548 | 54. 1549 | 130 CONTINUED: 130 1550 | It is a BOAT HOOK... 1551 | Collins PULLS HIMSELF THROUGH THE HOLE- ELBOWS FIRST- FORCING HIMSELF THROUGH- PUSHING OFF HIS SEAT- 1552 | UNDERWATER, HE PUSHES UP FROM THE SINKING PLANE... 1553 | 131 EXT. WATER, JUST OUTSIDE DUNKIRK HARBOUR - CONTINUOUS 131 1554 | Collins BREAKS the surface, GASPING, looks around- 1555 | A private yacht with a young man on the bow, boat hook extended... 1556 | Collins GRASPS the boat hook. 1557 | COLLINS (breathless) 1558 | Afternoon. 1559 | CUT TO: 1560 | 132 INT. COCKPIT, SPITFIRE 1 - DAY 132 1561 | Farrier comes around again, searching the sky for the German planes... 1562 | He looks down at the Destroyer- 1563 | It is LEAKING OIL FROM A LARGE HOLE IN ITS SIDE... 1564 | The dark OIL SLICK SPREADS QUICKLY ACROSS THE WATER- COVERING THE MEN IN THE WATER BETWEEN THE TRAWLER AND THE DESTROYER... 1565 | CUT TO: 1566 | 133 EXT. THE MOLE - DAY 133 1567 | The Destroyer BASILISK casts off. Men cover every available piece of deck. 1568 | Commander Bolton watches her wake. Colonel Winnant approaches- 1569 | COLONEL WINNANT We've wasted the day, Commander. 1570 | COMMANDER BOLTON 1571 | I share your frustration, Colonel. 1572 | They hear distant SHOTS- 1573 | Commander Bolton raises his FIELD GLASSES... trawler stuck in the shallows miles down the 1574 | he sees a blue beach. 1575 | COMMANDER BOLTON (CONT'D) Grounded trawler, taking fire. 1576 | (CONTINUED) 1577 | 1578 | 55. 1579 | 133 CONTINUED: 133 1580 | Colonel Winnant takes the field glasses... 1581 | COLONEL WINNANT 1582 | The enemy's breaking through the 1583 | dunes to the east. This is it. 1584 | 134 INT. HOLD, GROUNDED TRAWLER - DAY 134 1585 | Tommy comes up for air, GASPING, SPLUTTERING... 1586 | Water is POURING IN FROM DOZENS AND DOZENS OF HOLES... Alex comes up, COUGHING, with Gibson... 1587 | ALEX We're off-! 1588 | Alex crawls over to the stairs, climbs out into the well- 1589 | 135 EXT. TRAWLER - CONTINUOUS 135 1590 | Alex pokes his head out as the Seaman is sneaking up to see where they are headed- 1591 | The Dutch Seaman TURNS the wheel- JUMPS back onto the floor of the well as BULLETS IMPACT the cabin- 1592 | He throws the engine into FORWARD gear- Turns to Alex- 1593 | DUTCH SEAMAN THE HOLES! PLUG THE HOLES! 1594 | Alex crawls back downstairs- 1595 | 136 INT. HOLD, TRAWLER - CONTINUOUS 136 1596 | Alex FALLS down the stairs- 1597 | ALEX 1598 | PLUG THE HOLES! PLUG THE HOLES! 1599 | The men STUFF RAGS, BOLTS, FINGERS, ANYTHING they can lay hands on to plug as many holes as possible... 1600 | CUT TO: 1601 | 137 OMITTED 137 1602 | 138 INT. CABIN, MOONSTONE - MOMENTS LATER 138 1603 | Collins, drying himself with a blanket, looks down at George, whose breathing is shallow, sightless eyes open. 1604 | (CONTINUED) 1605 | 1606 | 56. 1607 | 138 CONTINUED: 138 1608 | COLLINS 1609 | (to Peter) 1610 | I don't really know, son. You were right not to move him. 1611 | (reassuring) 1612 | You've done the best for him you can. 1613 | 139 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - CONTINUOUS 139 The Shivering Soldier watches Collins come out on deck- 1614 | SHIVERING SOLDIER Is he alright? 1615 | PETER (O.S.) 1616 | No. 1617 | Peter is glaring at the Shivering Soldier. 1618 | PETER (CONT'D) No, he's not- 1619 | BOOM! Collins follows Mr.Dawson's gaze to a Destroyer up ahead being BOMBED by a Heinkel, HUGE PLUMES of water rising just beside her... 1620 | The Shivering Soldier retreats into himself- Peter runs up to the bow- 1621 | A BLUE FISHING TRAWLER a quarter of a mile off, sinking... 1622 | PETER (CONT'D) Dad, there's men in the water! 1623 | Mr.Dawson looks ahead to where Peter is pointing- 1624 | He puts the throttle forward, heading into the fray... 1625 | Collins spots Spitfire 1 arcing around, trying to get a bead on the Heinkel... 1626 | COLLINS Come on, Farrier... 1627 | CUT TO: 1628 | 140 INT. COCKPIT, SPITFIRE 1 - DAY 140 1629 | Farrier pulls on the stick, lining up behind a 109... 1630 | He FIRES, chasing down the plane, FIRING again... SMOKE from the German plane, which starts to drop... 1631 | Farrier is in a heavy DIVE, when HIS ENGINE CHOKES- 1632 | (CONTINUED) 1633 | 1634 | 57. 1635 | 140 CONTINUED: 140 1636 | Farrier's hand DARTS forward, SWITCHING TO HIS RESERVE TANK BEFORE THE ENGINE CAN DIE... 1637 | The engine CATCHES AGAIN- Farrier PULLS OUT OF THE DIVE... CUT TO: 1638 | 141 EXT. THE MOLE - DAY 141 1639 | Through the binoculars Colonel Winnant watches the Blue Trawler pushing out to sea, LOW in the water... 1640 | Commander Bolton watches a Destroyer, under full steam, heading out to the channel... 1641 | Where there are SHAPES OF BOATS ON THE HORIZON... 1642 | 142 EXT. DECK OF TRAWLER - CONTINUOUS 142 1643 | The Dutch Seaman aims the boat at a Destroyer out at the mouth of the harbour... 1644 | 143 INT. HOLD, TRAWLER - DAY 143 1645 | Tommy, Gibson, Alex, Highlander1 and the others STUFF the holes as best they can- the makeshift plugs POP OUT every few seconds- the soldiers SCRABBLE UNDER WATER to find them and STUFF them back in- hands PRESSED against water jets, SPRAY COMING IN EVERYWHERE... 1646 | 144 OMITTED 144 1647 | 145 EXT. DECK OF TRAWLER - CONTINUOUS 145 1648 | The Dutch Seaman looks over the rail, CONCERNED, to see how fast his boat is lowering into the swell... 1649 | CUT TO: 1650 | 146 OMITTED 146 1651 | 147 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - DAY 147 1652 | Collins watches Farrier spin around to get after the Heinkel- 1653 | The Moonstone comes up on the men in the water. Collins comes to the side, to help Peter fish men out- 1654 | Collins notices the surface of the water- 1655 | COLLINS (to Mr.Dawson) 1656 | Oil. We're getting into oil! 1657 | Mr.Dawson puts the screw into reverse, stopping the boat. 1658 | (CONTINUED) 1659 | 1660 | 58. 1661 | 147 CONTINUED: 147 1662 | They fish men out of the water- the men COVERED IN OIL, ANONYMOUS IN THEIR GLOSSY BLACK FILTH... 1663 | CUT TO: 1664 | 148 INT. COCKPIT, SPITFIRE 1 - DAY 148 1665 | Farrier chases down the Heinkel, closing in as its top turret OPENS UP ON HIM- TRACER FIRE LIGHTING UP ALL AROUND HIM- 1666 | HE DIVES DOWN UNDER THE RANGE OF THE REAR TURRET- THEN ANGLES UP, FIRING AT THE BOMBER'S TAIL... 1667 | CUT TO: 1668 | 149 EXT. BLUE TRAWLER - DAY 149 1669 | The Dutch Seaman sees water SLOSHING OVER THE DECK- 1670 | DUTCH SEAMAN ABANDON SHIP! ABANDON SHIP! 1671 | 150 INT. HOLD, TRAWLER - CONTINUOUS 150 1672 | The soldiers, holding back the water, cannot hear him... one by one they start to abandon the task- more and more water pouring in... Alex and Gibson are last- Alex TURNS, sees they are alone, GRABS Gibson by the shoulder then JUMPS for the exit- 1673 | Gibson, STILL HOLDING BACK THE WATER, notices too late- 1674 | 151 EXT. BLUE TRAWLER - CONTINUOUS 151 1675 | Tommy gets on deck- 1676 | Sees the Keith, a quarter of a mile away... 1677 | He DIVES INTO THE WATER, pulling away from the SWAMPED TRAWLER- 1678 | All the men dive off the sinking boat, swimming for the Keith... 1679 | 152 OMITTED 1680 | 153 INT. HOLD, TRAWLER - CONTINUOUS 1681 | Gibson DIVES for the exit- 1682 | He is BLASTED back by water- 1683 | DRAGGED DOWN with the sinking trawler... 1684 | 152 153 1685 | CUT TO: 1686 | 1687 | 154 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - DAY 154 1688 | Peter, Collins and the first oil-covered men pull more oily men from the water, the decks of the yacht rapidly filling- 1689 | Mr.Dawson looks at the oil slick, concerned. He addresses the oily survivors- 1690 | Below deck. 1691 | No fear. 1692 | MR.DAWSON 1693 | OILY SURVIVOR 1694 | MR.DAWSON 1695 | We need to get as many of you on 1696 | board as we can before the oil catches fire- get below or get off my boat- your choice. 1697 | The oily survivors head below decks- Peter runs back to the companionway to shout down- 1698 | PETER Careful there-! 1699 | 155 INT. CABIN, MOONSTONE - CONTINUOUS 155 Peter pokes his head down- 1700 | Sees two oily survivors moving George from the bottom of the steps- 1701 | PETER 1702 | Careful! 1703 | The oily survivors look up at him- Alex is one of them- 1704 | ALEX (quiet) 1705 | He's dead, mate. Peter takes this in... 1706 | PETER 1707 | So be bloody careful with him! 1708 | 155A EXT. THE MOLE - CONTINUOUS 1709 | Commander Bolton STARES at 1710 | He GRABS the field glasses to his eyes- 1711 | 155A 1712 | the shapes in the distance... from Colonel Winnant, puts them 1713 | BOATS. CIVILIAN BOATS. ALL SHAPES AND SIZES. AN ARMADA. Colonel Winnant PEERS over Commander Bolton's shoulder... 1714 | (CONTINUED) 1715 | 59. 1716 | 1717 | 60. 1718 | 155A CONTINUED: 155A 1719 | COLONEL WINNANT What can you see? 1720 | Commander Bolton slowly lowers the glasses. COMMANDER BOLTON 1721 | (gentle) Home. 1722 | Colonel Winnant grabs the glasses, confused... 1723 | 155B EXT. DECK OF A DESTROYER - CONTINUOUS 155B 1724 | The SOLDIERS peer over the railing at the absurd collection of vessels passing them in the opposite direction: 1725 | YACHTS, PADDLE STEAMERS, FISHING TRAWLERS, DAY SAILERS, FERRIES, DREDGERS, DINGHIES, ROW BOATS... 1726 | Crewed by: 1727 | FISHERMEN, MERCHANT NAVY SAILORS, NAVAL OFFICERS, CIVILIAN CREW, NAVAL CREW, NURSES, RETIRED SAILORS... 1728 | The EXHAUSTED SOLDIERS lining the decks of the Basilisk start to CLAP...then to CHEER... some are CRYING... 1729 | 156 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - CONTINUOUS 156 1730 | Peter steps out of the cabin, REELING. Meets his dad's questioning glance with unmistakable shock- 1731 | SHIVERING SOLDIER (O.S.) 1732 | The lad... 1733 | Peter turns. The Shivering Soldier is looking up at him with terrified eyes, blanket TIGHT around his shoulders. 1734 | SHIVERING SOLDIER (CONT'D) Will he be okay? 1735 | Peter looks at the Shivering Soldier. Sees the WHITE KNUCKLES clasping the edge of the rough blanket. Peter NODS. 1736 | The Shivering Soldier turns, staring out at the destroyer. 1737 | Peter catches Mr.Dawson looking at him. Approving. 1738 | Collins, pulling a man from the water, looks up at Spitfire 1 chasing down the Heinkel- 1739 | COLLINS Come on, Farrier... 1740 | CUT TO: 1741 | 1742 | 157 INT. COCKPIT, SPITFIRE 1 - DAY 157 Farrier strafes the Heinkel- no apparent effect- 1743 | Zipping over it, he dives down out of range of its turret- 1744 | BANKS hard left to line up for another shot- 1745 | A 109 CUTS across him- TRACER FIRE SHOOTING PAST- 1746 | CUT TO: 1747 | 158 EXT. WATER, JUST OUTSIDE DUNKIRK HARBOUR - DAY 158 1748 | Tommy SWIMS for the Keith... 1749 | He hears an airplane... looks up to see a Heinkel coming in over the Keith... the BOMBS DROP- 1750 | PLUMES OF WATER SHOOT UPWARDS ALL AROUND THE SHIP- Tommy DIVES under the water for protection- the EXPLOSIONS ARE DEAFENING- HE HOLDS HIS EARS WITH HIS HANDS- 1751 | Tommy BREAKS the surface- the barrage is over- the Keith is still afloat- Tommy swims for it- 1752 | Getting closer, Tommy realizes he's swimming in OIL, the black sludge covering his head and arms- he looks back- the blue trawler is gently slipping beneath the water... 1753 | Tommy makes for the Keith, even as he sees men JUMPING into the water from her decks... LIFEBOATS being lowered... 1754 | Tommy SPOTS another craft- a YACHT HEADING TOWARDS THEM- Tommy PULLS for the yacht as hard as he can... 1755 | CUT TO: 1756 | 158A EXT. BEACH AT MALO LES BAINS - CONTINUOUS 158A 1757 | The rag tag collection of SMALL SHIPS works the beach, picking men up in the shallows, ferrying them out to bigger ships... 1758 | Small open boats use the truck 'pier' to load men as the Engineer looks on with pride... 1759 | 159 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - DAY 159 1760 | The Moonstone is FILLED with oil-covered men- throughout the hold and across the decks- 1761 | MANY MORE still in the water... the Keith LISTS, men jump off the far side, away from the oil slick, where small ships are gathering to pick them up... 1762 | (CONTINUED) 1763 | 61. 1764 | 1765 | 62. 1766 | 159 CONTINUED: 159 1767 | Collins moves up the side- watching Farrier bank hard to get 1768 | behind the Heinkel- a 109 ZIPS across blazing... 1769 | Collins looks down at the oil-covered 160 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 1770 | his path, guns 1771 | water... 1772 | Farrier pulls around, HARD- the Heinkel is in side on, heading in for another run at Keith- 1773 | Farrier banks and pulls up to keep the bomber as he FIRES his cannons- 1774 | front of him, 1775 | in his sights 1776 | The HEINKEL CATCHES FIRE and starts FALLING... 1777 | 161 EXT. MOONSTONE, WATER OUTSIDE DUNKIRK HARBOUR - CONTINUOUS 161 1778 | Collins sees the Heinkel CATCH FIRE- TURNS to Mr.Dawson- 1779 | COLLINS 1780 | GO! GO! GO! 1781 | Mr.Dawson THROWS the engine into gear, TURNS the wheel- The Heinkel FALLS FLAMING TOWARDS THE OIL SLICK... 1782 | Peter has hold of one last oil-covered survivor, who hangs 1783 | on for dear life as the boat DRAGS HIM THROUGH THE OILY WATER- 1784 | The men left in the water shout with despair as the Moonstone motors away- 1785 | The FLAMING HEINKEL HITS THE WATER- EXPLODES- 1786 | THE SURFACE OF THE WATER CATCHES FIRE- SPREADING ACROSS THE WATER- MEN DUCK UNDERWATER TO ESCAPE THE FLAMES- 1787 | UNDERWATER: HIGHLANDER1 pushes down under, FIRE... the SURFACE IS AFLAME AS FAR AS HE 1788 | Peter HOLDS on to the oil-covered soldier- 1789 | Who is now being washed with cleaner water of the slick- 1790 | LOOKING UP AT THE CAN SEE... 1791 | as they COME out 1792 | As the oil comes off his face we see that IT IS TOMMY... 1793 | Collins watches, appalled, as the men in the water are engulfed by RELENTLESS FLAMES... the Keith is going down- survivors on the far side are picked up by the various SMALL SHIPS... 1794 | Under the water, HIGHLANDER1'S AIR RUNS OUT- the FLAMES RAGE ABOVE... 1795 | (CONTINUED) 1796 | 160 1797 | 1798 | 161 CONTINUED: 1799 | 161 1800 | HIS INSTINCT TO BREATH PUSHES HIM HE IS ENGULFED, SCREAMING, DYING- 1801 | Tommy lies on the deck at Peter's 1802 | TOMMY (a whisper) 1803 | Take me home. 1804 | 162 INT. COCKPIT, SPITFIRE 1 - DAY 1805 | Farrier sees the Heinkel EXPLODE, beaches... 1806 | UP INTO THE FLAMES WHERE 1807 | feet, eyes closed... 1808 | CUT TO: 1809 | turns away towards the 1810 | 162 1811 | He looks down at- 1812 | The thousands of men on the beach, 1813 | The small ships ferrying out to the larger vessels, The narrow mole with its endless rope of men... Farrier is awestruck... 1814 | He hears his engine start to SPUTTER... 1815 | It dies and the prop STOPS... 1816 | 163 EXT. THE MOLE - CONTINUOUS 163 1817 | Commander Bolton watches with satisfaction as a PADDLE STEAMER ties up... he calls up to a STEWARDESS(59)- 1818 | COMMANDER BOLTON Where're you from? 1819 | STEWARDESS Out of Dartmouth! 1820 | Bolton shakes his head in joyous disbelief. He watches men load into a SMALL OPEN SAILBOAT crewed by two YOUNG MEN- 1821 | COMMANDER BOLTON 1822 | From Deal? 1823 | (they nod) 1824 | Mind the current at the mouth, boys. 1825 | Bolton spots Spitfire 1- it SOARS overhead. He waves- 1826 | MALE VOICE (O.S.) Where've you been all my life?! 1827 | Commander Bolton SIGHS at this... then notices. No engine noise. 1828 | (CONTINUED) 1829 | 63. 1830 | 1831 | 64. 1832 | 163 CONTINUED: 163 1833 | He watches the spitfire, concerned, until- 1834 | Hears something BEHIND... another engine... a high whine... he turns to see- 1835 | A STUKA... 1836 | The men lining the mole shift restlessly. TRAPPED... 1837 | CUT TO: 1838 | 164 EXT. MOONSTONE, ENGLISH CHANNEL - EVENING 164 1839 | The Moonstone chugs along, low in the water, men laying down along her decks... 1840 | 165 INT. CABIN, MOONSTONE - CONTINUOUS 165 1841 | Men lie on every available space, packed in like sardines. Tommy catches sight of Alex looking at him. Tommy nods. 1842 | 166 EXT. MOONSTONE, ENGLISH CHANNEL - CONTINUOUS 166 Mr.Dawson is at the helm. Collins hears a distant engine- 1843 | COLLINS That's a fighter- 1844 | MR.DAWSON 1845 | ME 109, from the South. Peter, take 1846 | the wheel, listen for my instructions. 1847 | Mr.Dawson steps up onto the seat to look above the roof of the cabin... 1848 | Peter turns the wheel, the straightens up. Mr.Dawson 1849 | 167 EXT. THE MOLE - DAY 1850 | Moonstone swings to port, SPOTS THE 109, CLOSING... 1851 | MR.DAWSON (CONT'D) Point her south. 1852 | Commander Bolton turns to see the STUKA approaching, its distinctive kinked-wing silhouette bearing down like an AWFUL BIRD OF PREY... 1853 | The soldiers stir, some crouching, some closing their eyes. Commander Bolton takes a knee, BRACING. He bites his lip as the STUKA goes into its dive, that TERRIBLE WHINE BUILDING... 1854 | CUT TO: 1855 | CUT TO: 1856 | 167 1857 | 1858 | 168 EXT. MOONSTONE, ENGLISH CHANNEL - EVENING 168 Mr.Dawson STARES at the approaching 109- 1859 | MR.DAWSON Full speed ahead. 1860 | Peter throttles up- The 109 is GROWING- 1861 | Get ready 1862 | before he 1863 | his nose, 1864 | CLOSE NOW... 1865 | MR.DAWSON (CONT'D) 1866 | to pull hard to port... fires he'll have to lower I'll give you the signal... 1867 | Peter reaches over to the side of the wheel, ready to throw it- the 109 is practically upon them... 1868 | CUT TO: 1869 | 169 EXT. THE MOLE - DAY 169 1870 | Commander Bolton is mumbling a prayer as he watches the STUKA come at them- 1871 | BLAMBLAMBLAMBLAMBLAM!!! 1872 | THE STUKA IS STRAFED WITH FIRE AS SPITFIRE1 FLASHES PAST- 1873 | 170 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 170 1874 | Farrier darts past the STUKA, GLIDING, GUNS BLAZING... 1875 | 171 EXT. THE MOLE - CONTINUOUS 171 1876 | The STUKA never fires, it just SMASHES into the sea... 1877 | The soldiers all along the Mole CHEER. 1878 | 172 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 172 1879 | Farrier watches the STUKA disintegrate on the surface of the water. He nods... feeling the UNACCUSTOMED SILENCE... 1880 | CUT TO: 1881 | 173 EXT. MOONSTONE, ENGLISH CHANNEL - EVENING 173 1882 | Mr.Dawson STARES at the approaching 109- Peter glances back and forth between the 109 and his father... 1883 | MR.DAWSON 1884 | Wait for it... wait till he's 1885 | committed to his line... The nose of the 109 DIPS DOWN- 1886 | (CONTINUED) 1887 | 65. 1888 | 1889 | 66. 1890 | 173 CONTINUED: 173 1891 | MR.DAWSON (CONT'D) 1892 | NOW!! 1893 | Peter THROWS THE WHEEL, the Moonstone LURCHES TO PORT- 1894 | The GUNS ON THE 109 LIGHT UP, STRAFING THE The 109 FLASHES OVER... Collins watches it 1895 | COLLINS 1896 | He's off. 1897 | MR.DAWSON Bigger fish to fry. 1898 | Collins looks at Mr.Dawson. Curious. 1899 | COLLINS 1900 | How'd you know all that, anyway? 1901 | Mr.Dawson steps onto the deck. 1902 | MR.DAWSON 1903 | My son's one of you lot. I knew 1904 | he'd see us through. 1905 | Mr.Dawson moves forward. Collins steps up 1906 | COLLINS 1907 | You're RAF? 1908 | PETER 1909 | Not me. My brother. Flew Hurricanes. 1910 | He died third week into the war. 1911 | Collins looks forward at the proud father standing by the mast. 1912 | CUT TO: 1913 | 174 INT. COCKPIT, SPITFIRE 1 - DAY 174 1914 | Farrier sits in the silence, GLIDING... looking to see how far he might make it up the beach... 1915 | CUT TO: 1916 | 175 INT. CABIN, MOONSTONE - EVENING 175 1917 | Tommy gets to his feet, steps over other men as he slips over to the stairs... 1918 | 176 EXT. MOONSTONE, ENGLISH CHANNEL - EVENING Tommy and Alex poke their heads out- 1919 | 176 1920 | WATER TO STARBOARD- recede. 1921 | beside Peter. 1922 | (CONTINUED) 1923 | 1924 | 67. 1925 | 176 CONTINUED: 176 1926 | PETER Stay below, please. 1927 | TOMMY 1928 | We just want to see the cliffs- 1929 | Tommy looks over water. 1930 | Dover? Peter shakes his 1931 | at WHITE CLIFFS, ghostly above the dark 1932 | TOMMY (CONT'D) 1933 | head, amused. PETER 1934 | Weymouth. 1935 | Alex shakes his head, sadly. 1936 | ALEX 1937 | We let you all down, didn't we? 1938 | Peter just looks at this exhausted, ragged boy his own age. CUT TO: 1939 | 177 INT. COCKPIT, SPITFIRE 1 - DAY 177 1940 | Farrier GLIDES, banking, looking for a suitable stretch of beach to ditch... 1941 | In the strangely SILENT plane, he passes over the troops, lines up on the vast stretch of sand beyond Malo les Bains... 1942 | CUT TO: 1943 | 178 EXT. HARBOUR AT WEYMOUTH - NIGHT 178 1944 | Soldier after soldier climbs out of the yacht. The CORPORAL handing out travel chits marvels at the absurd amount... 1945 | CORPORAL 1946 | How many you got in there? 1947 | The Shivering Soldier is taken ashore, wrapped in blankets. 1948 | Tommy and Alex stick together as they are handed hot cups of tea and shepherded out of the harbour in long lines. 1949 | Peter supervises as George's body is taken ashore. 1950 | As Collins steps off the boat a soldier from another boat spots his RAF uniform- 1951 | SOLDIER 1952 | (furious) 1953 | Where the hell were you! 1954 | (CONTINUED) 1955 | 1956 | 68. 1957 | 178 CONTINUED: 178 1958 | Collins just stands there. He feels a hand on his shoulder. It is Mr.Dawson. He indicates the men filing off the Moonstone- 1959 | MR.DAWSON They know where you were. 1960 | Mr.Dawson puts his hat on. To go home. 1961 | 179 EXT. WEYMOUTH RAIL YARD - NIGHT 179 1962 | Tommy and Alex, exhausted, downcast, are herded across the tracks towards a train. Before getting on they are handed a BLANKET and cup of tea by an ELDERLY MAN, who looks at their hands not their faces as he hands the rough blanket over- 1963 | ELDERLY MAN 1964 | ...well done, lads... well done, 1965 | lads... 1966 | ALEX 1967 | All we did is survive. 1968 | ELDERLY MAN 1969 | That's enough. Well, done, lads, 1970 | well done, lads... 1971 | Alex steps up onto the train. The Elderly Man reaches out to Tommy, touching his face- clearly BLIND. 1972 | 180 INT. TRAIN - CONTINUOUS 180 1973 | Tommy FLOPS down, lying across the seat. Alex is slumped opposite, tears starting to roll down his cheeks. 1974 | ALEX 1975 | That old bloke wouldn't even look us 1976 | in the eye. 1977 | No response. He looks over- Tommy is already asleep. 1978 | CUT TO: 1979 | 181 EXT. THE MOLE - EVENING 181 1980 | The Mole is EMPTY but for bodies. 1981 | A PRIVATE opens his eyes. He sits up, alone on the deserted Mole, his comrades gone, mistaken for dead... 1982 | COMMANDER BOLTON (O.S.) Come on, then, private... 1983 | The Private looks down at the water to see Commander Bolton standing in a launch full of ARMY OFFICERS. 1984 | (CONTINUED) 1985 | 1986 | 69. 1987 | 181 CONTINUED: 181 1988 | COMMANDER BOLTON (CONT'D) I know we're officers, but it's us 1989 | or the enemy, so now's not the time to be particular... 1990 | The Private scrambles down into the launch, where Colonel Winnant stands talking to Commander Bolton. 1991 | They look out at the vast deserted beach, littered with corpses and abandoned equipment... 1992 | COLONEL WINNANT (to Commander Bolton) 1993 | Churchill got his 30 thousand. 1994 | COMMANDER BOLTON 1995 | And then some. Almost three hundred 1996 | thousand. So far. 1997 | Commander Bolton steps back up onto the mole. 1998 | So far? 1999 | COLONEL WINNANT 2000 | COMMANDER BOLTON 2001 | I'm staying. (off look) 2002 | For the French. 2003 | The Launch pulls away from Commander Bolton on the mole. 2004 | CUT TO: 2005 | 182 INT. TRAIN - MORNING 182 2006 | Sunlight FLICKERING on Tommy's eyelids wakes him. We have the sense that he has been asleep for a very long time. 2007 | The train full of soldiers rolls to a halt. Alex opens the window- spots a BOY near the tracks- 2008 | ALEX Hey! Where are we?! 2009 | BOY 2010 | Siding. You'll pull in in a minute- 2011 | ALEX What station? 2012 | BOY (surprised) 2013 | Woking. 2014 | Alex spots stacks of NEWSPAPERS waiting to be loaded. 2015 | (CONTINUED) 2016 | 2017 | 70. 2018 | 182 CONTINUED: 182 2019 | ALEX 2020 | Grab me one of them papers. 2021 | (the boy hesitates) Go on! 2022 | The boy pulls the paper off the top and stretches up to hand it to Alex. Alex slumps into his seat, the headline: 2023 | "CHURCHILL ADDRESSES DUNKIRK EVACUATION IN COMMONS" 2024 | Alex thrusts the paper at Tommy. 2025 | ALEX (CONT'D) 2026 | I can't bear it. You read it. 2027 | TOMMY Can't bear it? 2028 | ALEX 2029 | They'll be spitting at us in the 2030 | streets. If they're not locked up waiting for the invasion. 2031 | CUT TO: 2032 | 183 EXT. WEYMOUTH TOWN - DAY 183 2033 | Peter walks down the DESERTED HIGH STREET. He stops. Walks into the office of the local paper- the Herald... 2034 | 184 INT. HERALD OFFICE - CONTINUOUS 184 Peter hands the EDITOR a photograph. Of George. 2035 | CUT TO: 2036 | 185 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 185 2037 | Farrier CHECKS his CANOPY is locked... STOWS loose items... PUMPING the handle all the while... 2038 | 186 EXT. SPITFIRE 1 - DAY 2039 | 186 2040 | 187 2041 | The landing gear 2042 | 187 INT. TRAIN - DAY Tommy looks down 2043 | INCHES out of its housing... 2044 | at the paper. Starts to read. Poorly. TOMMY 2045 | "Wars are not won by evacuations." 2046 | Alex shakes his head at this. 2047 | The train starts to pull into the station... 2048 | (CONTINUED) 2049 | CUT TO: 2050 | 2051 | 71. 2052 | 187 CONTINUED: 187 2053 | The platform is CROWDED WITH CIVILIANS- Alex SLINKS DOWN into his seat, turning away from the window... 2054 | A CIVILIAN BANGS on the glass, PEERING in... 2055 | ALEX I can't look. 2056 | TOMMY 2057 | "But there was a victory inside this deliverance which should be noted..." 2058 | Alex turns- the Civilian GRINS, holding up two beer bottles. The platform is packed with CHEERING AND WAVING CIVILIANS... 2059 | Women with SANDWICHES and DRINKS rush up to the windows... 2060 | TOMMY (CONT'D) 2061 | "Our thankfulness at the escape of our army..." 2062 | Alex opens the window, GRABBING food and drink as Tommy continues to read... 2063 | TOMMY (CONT'D) 2064 | "Must not blind us to the fact that 2065 | what has happened in France... Is a colossal military disaster..." 2066 | 188 INT. COCKPIT, SPITFIRE 1 - DAY 188 Farrier PUMPS the handle- 2067 | 189 EXT. SPITFIRE - CONTINUOUS 189 The landing gear INCHES past HALFWAY DOWN... 2068 | 190 INT. COCKPIT, SPITFIRE 1 - CONTINUOUS 190 PUMPING the handle, Farrier checks his belts- 2069 | TOMMY (V.O.) 2070 | "...and we must expect another blow 2071 | to be struck almost immediately..." 2072 | He holds the plane steady in its descent towards the sands... 2073 | 191 EXT. BEACH AT LA PANNE - CONTINUOUS 191 2074 | Spitfire 1 SWOOPS onto the flat sand, WHEELS DOWN. 2075 | TOMMY (V.O.) 2076 | "We shall go on to the end, we shall 2077 | fight in France..," 2078 | Farrier SLIDES back the canopy and climbs out of the plane... 2079 | 2080 | 192 INT. MR.DAWSON HOME - DAY 192 2081 | Peter, gets up from the kitchen table. MRS.DAWSON is at the stove, her back to us. 2082 | As Peter grabs his coat he runs into Mr.Dawson, letters in hand, looking at the Herald. He hands it to Peter... 2083 | TOMMY (V.O.) 2084 | "We shall fight on the seas and 2085 | oceans..," 2086 | The small headline: 2087 | "LOCAL BOY, GEORGE MILLS, JUST 17, HERO AT DUNKIRK" Peter looks at his father. Nods with satisfaction. 2088 | 193 EXT. BEACH AT LA PANNE - DAY 193 Farrier brushes sand from the wing of his beloved Spitfire... 2089 | TOMMY (V.O.) "...We shall fight with growing 2090 | confidence and growing strength in the air..." 2091 | Farrier pulls his FLARE GUN... he SHOOTS INTO THE COCKPIT... 2092 | 194 INT. TRAIN - DAY 194 2093 | Alex hangs out the window, GUZZLING from a beer bottle, GRINNING at the women outside... 2094 | TOMMY 2095 | "We shall defend our Island-" 2096 | Alex turns, deliriously HAPPY, beer running down his chin- ALEX 2097 | What?! 2098 | TOMMY (louder, over the 2099 | celebration) 2100 | "...we shall defend our island, whatever the cost may be- we shall fight on the beaches, we shall fight on the landing grounds..." 2101 | 195 EXT. DUNKIRK HARBOUR - EVENING 195 Bodies gently bob in the water... 2102 | TOMMY (V.O.) 2103 | "We shall fight in the fields and 2104 | the streets..." 2105 | (CONTINUED) 2106 | 72. 2107 | 2108 | 73. 2109 | 195 CONTINUED: 195 2110 | Abandoned trucks and anti-aircraft guns, piles of boots, stacks of rifles catch the last light... 2111 | TOMMY (V.O.) (CONT'D) "We shall fight in the hills; we 2112 | shall never surrender..." 2113 | Bodies line the length of the mole... 2114 | TOMMY (V.O.) (CONT'D) "And even if, which I do not for a 2115 | moment believe, this island... were subjugated and starving..," 2116 | 196 EXT. BEACH AT LA PANNE - CONTINUOUS 196 2117 | Farrier KNEELS, HANDS ON HEAD, as DARK SHAPES OF GERMAN SOLDIERS (seen only from behind) surround him... 2118 | TOMMY (V.O.) 2119 | "...then our empire beyond the seas, 2120 | armed and guarded by the British fleet, would carry on the struggle..." 2121 | Farrier is led away from the BURNING PLANE... 2122 | 197 INT. TRAIN - DAY 197 2123 | Alex is oblivious. Tommy continues, to himself... TOMMY 2124 | "...until, in God's good time..." 2125 | 198 EXT. BEACH AT LA PANNE - TWILIGHT 198 Moving towards the BURNING SPITFIRE... 2126 | TOMMY (V.O.) 2127 | "...the New World, with all its power 2128 | and might..," 2129 | The shape of the plane is still visible beneath the FLAMES... 2130 | TOMMY (V.O.) (CONT'D) "...steps forth to the rescue and 2131 | the liberation of the old." 2132 | MOVE IN on the burning spitfire until the FLAMES FILL THE FRAME and we- 2133 | CUT TO BLACK. 2134 | CREDITS. 2135 | END. 2136 | -------------------------------------------------------------------------------- /Experiments/Cinema/cinema.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################################ 5 | # Experiment for Figures 13 and 14 in https://arxiv.org/pdf/1901.08949.pdf # 6 | ############################################################################ 7 | 8 | import numpy as np 9 | import pandas as pd 10 | import cupy as cp 11 | from collections import Counter 12 | from heapq import nlargest 13 | 14 | import sys 15 | sys.path.insert(0, "../") 16 | 17 | from SRW import SubspaceRobustWasserstein 18 | from Optimization.frankwolfe import FrankWolfe 19 | sys.path.insert(0, "Cinema/") 20 | 21 | 22 | ################################################## 23 | # Load the Word2Vec vectors using fasttext 24 | # Please download the file from: 25 | # https://fasttext.cc/docs/en/english-vectors.html 26 | ################################################## 27 | import io 28 | def load_vectors(fname, size=None): 29 | fin = io.open(fname, 'r', encoding='utf-8', newline='\n', errors='ignore') 30 | n, d = map(int, fin.readline().split()) 31 | data = {} 32 | i = 0 33 | for line in fin: 34 | if size and i >= size: 35 | break 36 | if i >= 2000: 37 | tokens = line.rstrip().split(' ') 38 | data[tokens[0]] = np.array(tokens[1:], dtype='f8') 39 | i += 1 40 | return data 41 | 42 | 43 | dictionnary = load_vectors('Data/dict.vec', size=20000) 44 | dictionnary_pd = pd.DataFrame(dictionnary).T 45 | 46 | 47 | ################################## 48 | # Text Preprocessing 49 | # And transformation into measures 50 | ################################## 51 | import string 52 | def textToMeasure(text): 53 | text = text.replace('\n', '') 54 | text = text.replace('\t', '') 55 | words = text.split(' ') 56 | table = str.maketrans('', '', string.punctuation.replace("'", "")) 57 | words = [w.translate(table) for w in words if len(w) > 0] 58 | words = [w for w in words if w in dictionnary.keys()] 59 | words = [w for w in words if not w[0].isupper()] 60 | words = [w for w in words if not w.isdigit()] 61 | cX = Counter(words) 62 | size = len(words) 63 | cX = Counter(words) 64 | words = list(set(words)) 65 | a = np.array([cX[w] for w in words])/size 66 | X = np.array([dictionnary[w] for w in words]) 67 | return X, a, words 68 | 69 | 70 | def load_text(file): 71 | """return X,a,words""" 72 | with open(file) as fp: 73 | text = fp.read() 74 | return textToMeasure(text) 75 | 76 | 77 | ############ 78 | # Plotting # 79 | ############ 80 | def plot_pushforwards_wordcloud(SRW, words_X, words_Y): 81 | """Plot the projected measures as word clouds.""" 82 | proj_X, proj_Y = SRW.get_projected_pushforwards() 83 | N_print_words = 30 84 | plt.figure(figsize=(10,10)) 85 | plt.scatter(proj_X[:,0], proj_X[:,1], s=X.shape[0]*20*a, c='r', zorder=10, alpha=0.) 86 | plt.scatter(proj_Y[:,0], proj_Y[:,1], s=Y.shape[0]*20*b, c='b', zorder=10, alpha=0.) 87 | large_a = nlargest(N_print_words, [a[words_X.index(i)] for i in words_X if i not in words_Y])[-1] 88 | large_b = nlargest(N_print_words, [b[words_Y.index(i)] for i in words_Y if i not in words_X])[-1] 89 | large_ab = nlargest(N_print_words, [0.5*a[words_X.index(i)] + 0.5*b[words_Y.index(i)] for i in words_Y if i in words_X])[-1] 90 | for i in range(a.shape[0]): 91 | if a[i] > large_a: 92 | if words_X[i] not in words_Y: 93 | plt.gca().annotate(words_X[i], proj_X[i,:], size=2500*a[i], color='b', ha='center', alpha=0.8) 94 | for j in range(b.shape[0]): 95 | if b[j] > large_b and words_Y[j] not in words_X: 96 | plt.gca().annotate(words_Y[j], proj_Y[j,:], size=2500*b[j], color='r', ha='center', alpha=0.8) 97 | elif words_Y[j] in words_X and 0.5*b[j]+0.5*a[words_X.index(words_Y[j])] > large_ab: 98 | size = 0.5*b[j] + 0.5*a[words_X.index(words_Y[j])] 99 | plt.gca().annotate(words_Y[j], proj_Y[j,:], size=2500*size, color='darkviolet', ha='center', alpha=0.8) 100 | plt.axis('equal') 101 | plt.axis('off') 102 | plt.show() 103 | 104 | 105 | 106 | ######################################################################### 107 | # COMPUTE SRW DISTANCES BETWEEN THE MOVIES 108 | # AND PLOT THE OPTIMAL SUBSPACE BETWEEN KILL BILL VOL.1 AND INTERSTELLAR 109 | ######################################################################### 110 | 111 | scripts = ['DUNKIRK.txt', 'GRAVITY.txt', 'INTERSTELLAR.txt', 'KILL_BILL_VOLUME_1.txt', 'KILL_BILL_VOLUME_2.txt', 'THE_MARTIAN.txt', 'TITANIC.txt'] 112 | Nb_scripts = len(scripts) 113 | SRW_matrix = cp.zeros((Nb_scripts, Nb_scripts)) 114 | measures = [] 115 | for film in scripts: 116 | measures.append(load_text('Data/'+film)) 117 | 118 | for film1 in scripts: 119 | for film2 in scripts: 120 | i = scripts.index(film1) 121 | j = scripts.index(film2) 122 | if i < j: 123 | X,a,words_X = measures[i] 124 | Y,b,words_Y = measures[j] 125 | algo = FrankWolfe(reg=0.1, step_size_0=None, max_iter=50, threshold=0.01, max_iter_sinkhorn=30, threshold_sinkhorn=1e-3, use_gpu=True) 126 | SRW = SubspaceRobustWasserstein(X, Y, a, b, algo, k=2) 127 | SRW.run() 128 | SRW.plot_convergence() 129 | SRW_matrix[i,j] = SRW.get_value() 130 | SRW_matrix[j,i] = SRW_matrix[i,j] 131 | print('SRW (', film1, ',', film2, ') =', SRW_matrix[i,j]) 132 | if film2 == 'KILL_BILL_VOLUME_1.txt' and film1 == 'INTERSTELLAR.txt': 133 | plot_pushforwards_wordcloud(SRW,words_X,words_Y) 134 | 135 | 136 | # Plot the metric MDS projection of the SRW values 137 | SRW_all = pd.DataFrame(SRW_matrix, index=scripts, columns=scripts) 138 | 139 | from sklearn.manifold import MDS 140 | embedding = MDS(n_components=2, dissimilarity='precomputed') 141 | dis = SRW_all-SRW_all[SRW_all>0].min().min() 142 | dis.values[[np.arange(dis.shape[0])]*2] = 0 143 | embedding = embedding.fit(dis) 144 | X = embedding.embedding_ 145 | 146 | import matplotlib.pyplot as plt 147 | plt.figure(figsize=(10,8)) 148 | plt.scatter(X[:,0], X[:,1], alpha=0.) 149 | plt.axis('equal') 150 | plt.axis('off') 151 | c = {'KILL_BILL_VOLUME_1.txt':'red', 'KILL_BILL_VOLUME_2.txt':'red', 'TITANIC.txt':'blue', 'DUNKIRK.txt':'blue', 'GRAVITY.txt':'black', 'INTERSTELLAR.txt':'black', 'THE_MARTIAN.txt':'black'} 152 | for film in scripts: 153 | i = scripts.index(film) 154 | plt.gca().annotate(film[:-4].replace('_', ' '), X[i], size=35, ha='center', color=c[film], weight="bold") 155 | plt.show() 156 | 157 | 158 | # Print the most similar movie to each movie 159 | for film in scripts: 160 | print('The film most similar to', film[:-4].replace('_', ' '), 'is', SRW_all[film].loc[SRW_all[film]>0].idxmin()[:-4].replace('_', ' ')) -------------------------------------------------------------------------------- /Experiments/Disk_to_annulus/disk_to_annulus.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################### 4 | # Experiment for Figure 6 in https://arxiv.org/pdf/1901.08949.pdf # 5 | ################################################################### 6 | 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | from SRW import SubspaceRobustWasserstein 12 | from Optimization.frankwolfe import FrankWolfe 13 | 14 | REAL_VALUE = (14./5) + (8./(5*np.sqrt(5)))*np.log((3+np.sqrt(5))/2) 15 | 16 | def disk_annulus(n,d,dim=2): 17 | 18 | a = (1./n) * np.ones(n) 19 | b = (1./n) * np.ones(n) 20 | 21 | # First measure : uniform on the unit disk 22 | angles = np.random.randn(n,dim) 23 | angles = (angles.T/np.linalg.norm(angles, axis=1)).T 24 | radii = np.random.uniform(0,1,size=n)**(1./dim) 25 | X = np.diag(radii).dot(angles) 26 | X = np.append(X, np.random.uniform(size=(n,d-dim)), axis=1) 27 | 28 | # Second measure : uniform on the annulus 2≤r≤3 29 | angles = np.random.randn(n,dim) 30 | angles = (angles.T/np.linalg.norm(angles, axis=1)).T 31 | 32 | radii = ((3**dim - 2**dim)*np.random.uniform(0, 1, size=n) + 2**dim)**(1./dim) 33 | Y = np.diag(radii).dot(angles) 34 | Y = np.append(Y, np.random.uniform(size=(n,d-dim)), axis=1) 35 | 36 | return a,b,X,Y 37 | 38 | n = 100 # Number of points for each measure 39 | d = 30 # Total dimension 40 | k = list(range(1,d+1)) # Compute SRW for all parameters 'k' 41 | nb_exp = 100 # Do 100 experiments 42 | dims = [2, 4, 7, 10] # Plot for 'true' dimension k* = 2, 4, 7, 10 43 | 44 | values = np.zeros((4, nb_exp, d)) 45 | for dim_index in range(4): 46 | dim = dims[dim_index] 47 | print('----') 48 | for t in range(nb_exp): 49 | print(dim,t) 50 | a,b,X,Y = disk_annulus(n,d,dim) 51 | FW = FrankWolfe(reg=0.2, step_size_0=None, max_iter=15, threshold=0.01, max_iter_sinkhorn=50, threshold_sinkhorn=10e-4, use_gpu=True) 52 | SRW = SubspaceRobustWasserstein(X, Y, a, b, FW, k) 53 | SRW.run() 54 | values[dim_index, t,:] = np.sort(list(SRW.get_value().values())) 55 | 56 | values_mean = np.mean(values, axis=1) 57 | values_min = np.min(values, axis=1) 58 | values_10 = np.percentile(values, 10, axis=1) 59 | values_25 = np.percentile(values, 25, axis=1) 60 | values_75 = np.percentile(values, 75, axis=1) 61 | values_90 = np.percentile(values, 90, axis=1) 62 | values_max = np.max(values, axis=1) 63 | 64 | plt.figure(figsize=(17,6)) 65 | col = [] 66 | for dim_index in range(4): 67 | mean, = plt.plot(range(1,d+1), values_mean[dim_index], label='Dimension $'+str(dims[dim_index])+'$', lw=4) 68 | col.append(mean.get_color()) 69 | plt.fill_between(range(1,d+1), values_25[dim_index], values_75[dim_index], facecolor=col[-1], alpha=0.3) 70 | plt.fill_between(range(1,d+1), values_10[dim_index], values_90[dim_index], facecolor=col[-1], alpha=0.2) 71 | plt.fill_between(range(1,d+1), values_min[dim_index], values_max[dim_index], facecolor=col[-1], alpha=0.15) 72 | plt.xlabel('Dimension', fontsize=25) 73 | plt.ylabel('SRW value', fontsize=25) 74 | plt.xticks(range(1,d+1), fontsize=20) 75 | ymax = plt.gca().get_ylim()[1] 76 | for dim_index in range(4): 77 | plt.gca().get_xticklabels()[dims[dim_index]-1].set_color(col[dim_index]) 78 | plt.axvline(x=dims[dim_index], ymax=(values_mean[dim_index,dims[dim_index]]/ymax)-0.055, c=col[dim_index], ls='--') 79 | plt.yticks(fontsize=20) 80 | plt.legend(fontsize=20) 81 | plt.grid(ls=':') 82 | plt.show() -------------------------------------------------------------------------------- /Experiments/Disk_to_annulus/disk_to_annulus_dependence_on_n.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ########################################################################## 5 | # Experiment for Figures 7 and 8 in https://arxiv.org/pdf/1901.08949.pdf # 6 | ########################################################################## 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | import cupy as cp 11 | 12 | from SRW import SubspaceRobustWasserstein 13 | from Optimization.frankwolfe import FrankWolfe 14 | 15 | 16 | 17 | REAL_VALUE = (14./5) + (8./(5*np.sqrt(5)))*np.log((3+np.sqrt(5))/2) 18 | 19 | def disk_annulus(n,d,dim=2): 20 | 21 | a = (1./n) * np.ones(n) 22 | b = (1./n) * np.ones(n) 23 | 24 | # First measure : uniform on the unit disk 25 | angles = np.random.randn(n,dim) 26 | angles = (angles.T/np.linalg.norm(angles, axis=1)).T 27 | radii = np.random.uniform(0,1,size=n)**(1./dim) 28 | X = np.diag(radii).dot(angles) 29 | X = np.append(X, np.random.uniform(size=(n,d-dim)), axis=1) 30 | 31 | # Second measure : uniform on the annulus 2≤r≤3 32 | angles = np.random.randn(n,dim) 33 | angles = (angles.T/np.linalg.norm(angles, axis=1)).T 34 | 35 | radii = ((3**dim - 2**dim)*np.random.uniform(0, 1, size=n) + 2**dim)**(1./dim) 36 | Y = np.diag(radii).dot(angles) 37 | Y = np.append(Y, np.random.uniform(size=(n,d-dim)), axis=1) 38 | 39 | return a,b,X,Y 40 | 41 | 42 | d = 30 # Total dimension 43 | k = 2 # k* = 2 and compute SRW with k = 2 44 | nb_exp = 500 # Do 500 experiments 45 | ns = [25, 50, 100, 250, 500, 1000] # Compute SRW between measures with 'n' points for 'n' in 'ns' 46 | 47 | values = np.zeros((len(ns), nb_exp)) 48 | values_subspace = np.zeros((len(ns), nb_exp)) 49 | 50 | proj = cp.zeros((d,d)) # Real optimal subspace 51 | proj[0,0] = 1 52 | proj[1,1] = 1 53 | for indn in range(len(ns)): 54 | n = ns[indn] 55 | # Sample nb_exp times 56 | for t in range(nb_exp): 57 | FW = FrankWolfe(reg=0.2, step_size_0=None, max_iter=15, threshold=0.01, max_iter_sinkhorn=30, threshold_sinkhorn=10e-04, use_gpu=False) 58 | a,b,X,Y = disk_annulus(n,d,dim=2) 59 | SRW_FW = SubspaceRobustWasserstein(X, Y, a, b, FW, k) 60 | SRW_FW.run() 61 | values[indn, t] = np.abs(REAL_VALUE-SRW_FW.get_value()) 62 | values_subspace[indn, t] = cp.linalg.norm(SRW_FW.get_Omega() - proj) 63 | 64 | print('n =',n,'/', np.mean(values[indn,:])) 65 | 66 | 67 | values_mean = np.mean(values, axis=1) 68 | values_min = np.min(values, axis=1) 69 | values_10 = np.percentile(values, 10, axis=1) 70 | values_25 = np.percentile(values, 25, axis=1) 71 | values_75 = np.percentile(values, 75, axis=1) 72 | values_90 = np.percentile(values, 90, axis=1) 73 | values_max = np.max(values, axis=1) 74 | 75 | 76 | import matplotlib.ticker as ticker 77 | plt.figure(figsize=(17,6)) 78 | mean, = plt.loglog(ns, values_mean, 'o-', lw=4, ms=11) 79 | col = mean.get_color() 80 | plt.fill_between(ns, values_25, values_75, facecolor=col, alpha=0.3) 81 | plt.fill_between(ns, values_10, values_90, facecolor=col, alpha=0.2) 82 | 83 | plt.xlabel('Number of points', fontsize=25) 84 | plt.ylabel('$|W^2(\mu,\\nu) - S^2(\hat\mu, \hat\\nu)|$', fontsize=25) 85 | plt.xticks(ns, fontsize=20) 86 | plt.yticks([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1], fontsize=20) 87 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.0f')) 88 | plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f')) 89 | plt.grid(ls=':') 90 | plt.show() 91 | 92 | 93 | 94 | values_subspace_mean = np.mean(values_subspace, axis=1) 95 | values_subspace_min = np.min(values_subspace, axis=1) 96 | values_subspace_10 = np.percentile(values_subspace, 10, axis=1) 97 | values_subspace_25 = np.percentile(values_subspace, 25, axis=1) 98 | values_subspace_75 = np.percentile(values_subspace, 75, axis=1) 99 | values_subspace_90 = np.percentile(values_subspace, 90, axis=1) 100 | values_subspace_max = np.max(values_subspace, axis=1) 101 | 102 | plt.figure(figsize=(17,6)) 103 | mean, = plt.loglog(ns, values_subspace_mean, 'o-', lw=4, ms=11) 104 | col = mean.get_color() 105 | plt.fill_between(ns, values_subspace_25, values_subspace_75, facecolor=col, alpha=0.3) 106 | plt.fill_between(ns, values_subspace_10, values_subspace_90, facecolor=col, alpha=0.2) 107 | plt.fill_between(ns, values_subspace_min, values_subspace_max, facecolor=col, alpha=0.15) 108 | 109 | plt.xlabel('Number of points', fontsize=25) 110 | plt.ylabel('$||\Omega^* - \widehat\Omega||_F$', fontsize=25) 111 | plt.xticks(ns, fontsize=20) 112 | plt.yticks([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8], fontsize=20) 113 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.0f')) 114 | plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f')) 115 | plt.grid(ls=':') 116 | plt.show() -------------------------------------------------------------------------------- /Experiments/Fragmented_hypercube/fragmented_hypercube.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################### 4 | # Experiment for Figure 2 in https://arxiv.org/pdf/1901.08949.pdf # 5 | ################################################################### 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | 10 | from SRW import SubspaceRobustWasserstein 11 | from Optimization.frankwolfe import FrankWolfe 12 | 13 | 14 | def T(x,d,dim=2): 15 | assert dim <= d 16 | assert dim >= 1 17 | assert dim == int(dim) 18 | return x + 2*np.sign(x)*np.array(dim*[1]+(d-dim)*[0]) 19 | 20 | def fragmented_hypercube(n,d,dim): 21 | assert dim <= d 22 | assert dim >= 1 23 | assert dim == int(dim) 24 | 25 | a = (1./n) * np.ones(n) 26 | b = (1./n) * np.ones(n) 27 | 28 | # First measure : uniform on the hypercube 29 | X = np.random.uniform(-1, 1, size=(n,d)) 30 | 31 | # Second measure : fragmentation 32 | Y = T(np.random.uniform(-1, 1, size=(n,d)), d, dim) 33 | 34 | return a,b,X,Y 35 | 36 | 37 | n = 100 # Number of points for each measure 38 | d = 30 # Total dimension 39 | k = list(range(1,d+1)) # Compute SRW for all parameters 'k' 40 | nb_exp = 100 # Do 100 experiments 41 | dims = [2, 4, 7, 10] # Plot for 'true' dimension k* = 2, 4, 7, 10 42 | 43 | values = np.zeros((4, nb_exp, d)) 44 | for dim_index in range(4): 45 | dim = dims[dim_index] 46 | for t in range(nb_exp): 47 | a,b,X,Y = fragmented_hypercube(n,d,dim) 48 | FW = FrankWolfe(reg=0.2, step_size_0=None, max_iter=15, threshold=0.01, max_iter_sinkhorn=50, threshold_sinkhorn=10e-4, use_gpu=True) 49 | SRW = SubspaceRobustWasserstein(X, Y, a, b, FW, k) 50 | SRW.run() 51 | values[dim_index, t,:] = np.sort(list(SRW.get_value().values())) 52 | 53 | 54 | values_mean = np.mean(values, axis=1) 55 | values_min = np.min(values, axis=1) 56 | values_10 = np.percentile(values, 10, axis=1) 57 | values_25 = np.percentile(values, 25, axis=1) 58 | values_75 = np.percentile(values, 75, axis=1) 59 | values_90 = np.percentile(values, 90, axis=1) 60 | values_max = np.max(values, axis=1) 61 | 62 | 63 | plt.figure(figsize=(17,6)) 64 | col = [] 65 | 66 | for dim_index in range(4): 67 | mean, = plt.plot(range(1,d+1), values_mean[dim_index], label='Dimension $'+str(dims[dim_index])+'$', lw=4) 68 | col.append(mean.get_color()) 69 | plt.fill_between(range(1,d+1), values_25[dim_index], values_75[dim_index], facecolor=col[-1], alpha=0.3) 70 | plt.fill_between(range(1,d+1), values_10[dim_index], values_90[dim_index], facecolor=col[-1], alpha=0.2) 71 | plt.fill_between(range(1,d+1), values_min[dim_index], values_max[dim_index], facecolor=col[-1], alpha=0.15) 72 | plt.xlabel('Dimension', fontsize=25) 73 | plt.ylabel('SRW value', fontsize=25) 74 | plt.xticks(range(1,d+1), fontsize=20) 75 | ymax = plt.gca().get_ylim()[1] 76 | for dim_index in range(4): 77 | plt.gca().get_xticklabels()[dims[dim_index]-1].set_color(col[dim_index]) 78 | plt.axvline(x=dims[dim_index], ymax=(values_mean[dim_index,dims[dim_index]]/ymax)-0.025, c=col[dim_index], ls='--') 79 | plt.yticks(fontsize=20) 80 | plt.legend(fontsize=20) 81 | plt.grid(ls=':') 82 | plt.show() -------------------------------------------------------------------------------- /Experiments/Fragmented_hypercube/fragmented_hypercube_dependence_on_n.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ########################################################################## 5 | # Experiment for Figures 3 and 4 in https://arxiv.org/pdf/1901.08949.pdf # 6 | ########################################################################## 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | import cupy as cp 11 | 12 | from SRW import SubspaceRobustWasserstein 13 | from Optimization.frankwolfe import FrankWolfe 14 | 15 | 16 | def T(x,d,dim=2): 17 | assert dim <= d 18 | assert dim >= 1 19 | assert dim == int(dim) 20 | return x + 2*np.sign(x)*np.array(dim*[1]+(d-dim)*[0]) 21 | 22 | def fragmented_hypercube(n,d,dim): 23 | assert dim <= d 24 | assert dim >= 1 25 | assert dim == int(dim) 26 | 27 | a = (1./n) * np.ones(n) 28 | b = (1./n) * np.ones(n) 29 | 30 | # First measure : uniform on the hypercube 31 | X = np.random.uniform(-1, 1, size=(n,d)) 32 | 33 | # Second measure : fragmentation 34 | Y = T(np.random.uniform(-1, 1, size=(n,d)), d, dim) 35 | 36 | return a,b,X,Y 37 | 38 | 39 | d = 30 # Total dimension 40 | k = 2 # k* = 2 and compute SRW with k = 2 41 | nb_exp = 500 # Do 500 experiments 42 | ns = [25, 50, 100, 250, 500, 1000] # Compute SRW between measures with 'n' points for 'n' in 'ns' 43 | 44 | values = np.zeros((len(ns), nb_exp)) 45 | values_subspace = np.zeros((len(ns), nb_exp)) 46 | 47 | proj = cp.zeros((d,d)) # Real optimal subspace 48 | proj[0,0] = 1 49 | proj[1,1] = 1 50 | for indn in range(len(ns)): 51 | n = ns[indn] 52 | # Sample nb_exp times 53 | for t in range(nb_exp): 54 | FW = FrankWolfe(reg=0.2, step_size_0=None, max_iter=15, threshold=0.01, max_iter_sinkhorn=30, threshold_sinkhorn=10e-04, use_gpu=True) 55 | a,b,X,Y = fragmented_hypercube(n,d,dim=2) 56 | SRW_FW = SubspaceRobustWasserstein(X, Y, a, b, FW, k) 57 | SRW_FW.run() 58 | values[indn, t] = np.abs(8-SRW_FW.get_value()) 59 | values_subspace[indn, t] = cp.linalg.norm(SRW_FW.get_Omega() - proj) 60 | 61 | print('n =',n,'/', np.mean(values[indn,:])) 62 | 63 | 64 | values_mean = np.mean(values, axis=1) 65 | values_min = np.min(values, axis=1) 66 | values_10 = np.percentile(values, 10, axis=1) 67 | values_25 = np.percentile(values, 25, axis=1) 68 | values_75 = np.percentile(values, 75, axis=1) 69 | values_90 = np.percentile(values, 90, axis=1) 70 | values_max = np.max(values, axis=1) 71 | 72 | 73 | import matplotlib.ticker as ticker 74 | plt.figure(figsize=(17,6)) 75 | mean, = plt.loglog(ns, values_mean, 'o-', lw=4, ms=11) 76 | col = mean.get_color() 77 | plt.fill_between(ns, values_25, values_75, facecolor=col, alpha=0.3) 78 | plt.fill_between(ns, values_10, values_90, facecolor=col, alpha=0.2) 79 | 80 | plt.xlabel('Number of points', fontsize=25) 81 | plt.ylabel('$|W^2(\mu,\\nu) - S^2(\hat\mu, \hat\\nu)|$', fontsize=25) 82 | plt.xticks(ns, fontsize=20) 83 | plt.yticks(np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0]), fontsize=20) 84 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.0f')) 85 | plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f')) 86 | plt.grid(ls=':') 87 | plt.show() 88 | 89 | 90 | 91 | values_subspace_mean = np.mean(values_subspace, axis=1) 92 | values_subspace_min = np.min(values_subspace, axis=1) 93 | values_subspace_10 = np.percentile(values_subspace, 10, axis=1) 94 | values_subspace_25 = np.percentile(values_subspace, 25, axis=1) 95 | values_subspace_75 = np.percentile(values_subspace, 75, axis=1) 96 | values_subspace_90 = np.percentile(values_subspace, 90, axis=1) 97 | values_subspace_max = np.max(values_subspace, axis=1) 98 | 99 | plt.figure(figsize=(17,6)) 100 | mean, = plt.loglog(ns, values_subspace_mean, 'o-', lw=4, ms=11) 101 | col = mean.get_color() 102 | plt.fill_between(ns, values_subspace_25, values_subspace_75, facecolor=col, alpha=0.3) 103 | plt.fill_between(ns, values_subspace_10, values_subspace_90, facecolor=col, alpha=0.2) 104 | plt.fill_between(ns, values_subspace_min, values_subspace_max, facecolor=col, alpha=0.15) 105 | 106 | plt.xlabel('Number of points', fontsize=25) 107 | plt.ylabel('$||\Omega^* - \widehat\Omega||_F$', fontsize=25) 108 | plt.xticks(ns, fontsize=20) 109 | plt.yticks(np.array(range(1,8))/10, fontsize=20) 110 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.0f')) 111 | plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f')) 112 | plt.grid(ls=':') 113 | plt.show() 114 | -------------------------------------------------------------------------------- /Experiments/Noise/noise.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | #################################################################### 4 | # Experiment for Figure 10 in https://arxiv.org/pdf/1901.08949.pdf # 5 | #################################################################### 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | 10 | from SRW import SubspaceRobustWasserstein 11 | from Optimization.projectedascent import ProjectedGradientAscent 12 | 13 | noise_level = 1 14 | d = 20 # Total dimension 15 | n = 100 # Number of points for each measure 16 | l = 5 # Dimension of Wishart 17 | nb_exp = 100 # Number of experiments 18 | k = list(range(1,d+1)) # Compute SRW for all dimension parameter k 19 | 20 | # Save the values 21 | no_noise = np.zeros((nb_exp,d)) 22 | noise = np.zeros((nb_exp,d)) 23 | 24 | 25 | for t in range(nb_exp): # Fore each experiment 26 | print(t) 27 | 28 | a = (1./n) * np.ones(n) 29 | b = (1./n) * np.ones(n) 30 | 31 | mean_1 = 0.*np.random.randn(d) 32 | mean_2 = 0.*np.random.randn(d) 33 | 34 | cov_1 = np.random.randn(d,l) 35 | cov_1 = cov_1.dot(cov_1.T) 36 | cov_2 = np.random.randn(d,l) 37 | cov_2 = cov_2.dot(cov_2.T) 38 | 39 | # Draw measures 40 | X = np.random.multivariate_normal(mean_1, cov_1, size=n) 41 | Y = np.random.multivariate_normal(mean_2, cov_2, size=n) 42 | 43 | # Add noise 44 | Xe = X + noise_level*np.random.randn(n,d) 45 | Ye = Y + noise_level*np.random.randn(n,d) 46 | 47 | # Compute SRW begtween X and Y 48 | algo = ProjectedGradientAscent(reg=0., step_size_0=0.01, max_iter=30, threshold=0.01, max_iter_sinkhorn=30, threshold_sinkhorn=10e-04, use_gpu=False) 49 | SRW = SubspaceRobustWasserstein(X, Y, a, b, algo, k) 50 | SRW.run() 51 | no_noise[t,:] = np.sort(list(SRW.get_value().values())) 52 | 53 | # Compute SRW begtween Xe and Ye 54 | algo = ProjectedGradientAscent(reg=0., step_size_0=0.01, max_iter=30, threshold=0.01, max_iter_sinkhorn=30, threshold_sinkhorn=10e-04, use_gpu=False) 55 | SRWe = SubspaceRobustWasserstein(Xe, Ye, a, b, algo, k) 56 | SRWe.run() 57 | noise[t,:] = np.sort(list(SRWe.get_value().values())) 58 | 59 | no_noise[t,:] /= no_noise[t,(d-1)] 60 | noise[t,:] /= noise[t,(d-1)] 61 | 62 | 63 | no_noise_mean = np.mean(no_noise, axis=0) 64 | no_noise_min = np.min(no_noise, axis=0) 65 | no_noise_10 = np.percentile(no_noise, 10, axis=0) 66 | no_noise_25 = np.percentile(no_noise, 25, axis=0) 67 | no_noise_75 = np.percentile(no_noise, 75, axis=0) 68 | no_noise_90 = np.percentile(no_noise, 90, axis=0) 69 | no_noise_max = np.max(no_noise, axis=0) 70 | 71 | noise_mean = np.mean(noise, axis=0) 72 | noise_min = np.min(noise, axis=0) 73 | noise_10 = np.percentile(noise, 10, axis=0) 74 | noise_25 = np.percentile(noise, 25, axis=0) 75 | noise_75 = np.percentile(noise, 75, axis=0) 76 | noise_90 = np.percentile(noise, 90, axis=0) 77 | noise_max = np.max(noise, axis=0) 78 | 79 | 80 | # PLOT 81 | plt.figure(figsize=(17,6)) 82 | 83 | plotnonoise, = plt.plot(range(d), no_noise_mean, label='Without Noise', lw=8) 84 | col_nonoise = plotnonoise.get_color() 85 | plt.fill_between(range(d), no_noise_25, no_noise_75, facecolor=col_nonoise, alpha=0.3) 86 | plt.fill_between(range(d), no_noise_10, no_noise_90, facecolor=col_nonoise, alpha=0.2) 87 | plt.fill_between(range(d), no_noise_min, no_noise_max, facecolor=col_nonoise, alpha=0.15) 88 | 89 | plotnoise, = plt.plot(range(d), noise_mean, label='With Noise', lw=8) 90 | col_noise = plotnoise.get_color() 91 | plt.fill_between(range(d), noise_25, noise_75, facecolor=col_noise, alpha=0.3) 92 | plt.fill_between(range(d), noise_10, noise_90, facecolor=col_noise, alpha=0.2) 93 | plt.fill_between(range(d), noise_min, noise_max, facecolor=col_noise, alpha=0.15) 94 | 95 | plt.xlabel('Dimension', fontsize=25) 96 | plt.ylabel('Normalized SRW value', fontsize=25) 97 | 98 | plt.yticks(fontsize=20) 99 | plt.xticks(range(d),range(1,d+1), fontsize=20) 100 | 101 | plt.legend(loc='best', fontsize=20) 102 | plt.grid(ls=':') 103 | plt.show() -------------------------------------------------------------------------------- /Experiments/Noise/noise_level.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | #################################################################### 5 | # Experiment for Figure 11 in https://arxiv.org/pdf/1901.08949.pdf # 6 | #################################################################### 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | from SRW import SubspaceRobustWasserstein 12 | from Optimization.projectedascent import ProjectedGradientAscent 13 | 14 | 15 | d = 20 # Total dimension 16 | n = 100 # Number of points in each measure 17 | k = 5 # Dimension of the Wishart (i.e. of support of the measures) 18 | nb_exp = 100 # Number of experiments to run 19 | reg = 0. # No regularization 20 | max_iter = 1000 # Maximum number of iterations (the bigger the more precise) 21 | thr = 1e-5 # Stopping threshold (not attained here since we are in unregularized SRW) 22 | 23 | a = (1./n) * np.ones(n) 24 | b = (1./n) * np.ones(n) 25 | 26 | mean_1 = np.zeros(d) 27 | mean_2 = np.zeros(d) 28 | 29 | # Noise levels to test 30 | ind = [0., 0.01, 0.1, 1, 2, 4, 7, 10] 31 | 32 | SRW = np.zeros((nb_exp, len(ind))) 33 | W = np.zeros((nb_exp, len(ind))) 34 | 35 | 36 | for t in range(nb_exp): 37 | print(t) 38 | cov_1 = np.random.randn(d,k) 39 | cov_1 = cov_1.dot(cov_1.T) 40 | cov_2 = np.random.randn(d,k) 41 | cov_2 = cov_2.dot(cov_2.T) 42 | 43 | # Draw the measures 44 | X = np.random.multivariate_normal(mean_1, cov_1, size=n) 45 | Y = np.random.multivariate_normal(mean_2, cov_2, size=n) 46 | 47 | lst_rsw = [] 48 | lst_w = [] 49 | for epsilon in ind: 50 | # Add noise of level epsilon 51 | noiseX = np.random.randn(n,d) 52 | noiseY = np.random.randn(n,d) 53 | Xe = X + epsilon*noiseX 54 | Ye = Y + epsilon*noiseY 55 | 56 | # Choice of step size 57 | ones = np.ones((n,n)) 58 | C = np.diag(np.diag(Xe.dot(Xe.T))).dot(ones) + ones.dot(np.diag(np.diag(Ye.dot(Ye.T)))) - 2*Xe.dot(Ye.T) 59 | step_size_0 = 1./np.max(C) 60 | 61 | # Compute SRW 62 | algo = ProjectedGradientAscent(reg=reg, step_size_0=step_size_0, max_iter=max_iter, max_iter_sinkhorn=50, threshold=thr, threshold_sinkhorn=1e-04, use_gpu=False) 63 | SRW_ = SubspaceRobustWasserstein(Xe, Ye, a, b, algo, k=k) 64 | SRW_.run() 65 | 66 | # Compute Wasserstein 67 | algo = ProjectedGradientAscent(reg=reg, step_size_0=step_size_0, max_iter=1, max_iter_sinkhorn=50, threshold=0.05, threshold_sinkhorn=1e-04, use_gpu=False) 68 | W_ = SubspaceRobustWasserstein(Xe, Ye, a, b, algo, k=d) 69 | W_.run() 70 | 71 | lst_rsw.append(SRW_.get_value()) 72 | lst_w.append(W_.get_value()) 73 | 74 | SRW[t,:] = np.array(lst_rsw) 75 | W[t,:] = np.array(lst_w) 76 | 77 | # Relative change 78 | SRW_percent = np.abs(SRW-np.array([SRW[:,0],]*len(ind)).transpose())/np.array([SRW[:,0],]*len(ind)).transpose() 79 | W_percent = np.abs(W-np.array([W[:,0],]*len(ind)).transpose())/np.array([W[:,0],]*len(ind)).transpose() 80 | 81 | SRW_percent = SRW_percent[:,1:] 82 | W_percent = W_percent[:,1:] 83 | 84 | SRW_mean = np.mean(SRW_percent, axis=0) 85 | SRW_min = np.min(SRW_percent, axis=0) 86 | SRW_10 = np.percentile(SRW_percent, 10, axis=0) 87 | SRW_25 = np.percentile(SRW_percent, 25, axis=0) 88 | SRW_75 = np.percentile(SRW_percent, 75, axis=0) 89 | SRW_90 = np.percentile(SRW_percent, 90, axis=0) 90 | SRW_max = np.max(SRW_percent, axis=0) 91 | 92 | W_mean = np.mean(W_percent, axis=0) 93 | W_min = np.min(W_percent, axis=0) 94 | W_10 = np.percentile(W_percent, 10, axis=0) 95 | W_25 = np.percentile(W_percent, 25, axis=0) 96 | W_75 = np.percentile(W_percent, 75, axis=0) 97 | W_90 = np.percentile(W_percent, 90, axis=0) 98 | W_max = np.max(W_percent, axis=0) 99 | 100 | 101 | # PLOT 102 | import matplotlib.ticker as ticker 103 | plt.figure(figsize=(17,6)) 104 | 105 | plotW, = plt.loglog(ind[1:], W_mean, 'o-', label='Wasserstein', lw=8, ms=20) 106 | col_W = plotW.get_color() 107 | plt.fill_between(ind[1:], W_25, W_75, facecolor=col_W, alpha=0.3) 108 | plt.fill_between(ind[1:], W_10, W_90, facecolor=col_W, alpha=0.2) 109 | 110 | plotSRW, = plt.loglog(ind[1:], SRW_mean, 'o-', label='Subspace Robust Wasserstein', lw=8, ms=20) 111 | col_SRW = plotSRW.get_color() 112 | plt.fill_between(ind[1:], SRW_25, SRW_75, facecolor=col_SRW, alpha=0.3) 113 | plt.fill_between(ind[1:], SRW_10, SRW_90, facecolor=col_SRW, alpha=0.2) 114 | 115 | plt.xlabel('Noise level (log scale)', fontsize=25) 116 | plt.ylabel('Relative error (log scale)', fontsize=25) 117 | 118 | 119 | plt.yticks(fontsize=20) 120 | plt.xticks(ind[1:], fontsize=20) 121 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.2g')) 122 | 123 | plt.legend(loc=2, fontsize=25) 124 | plt.grid(ls=':') 125 | plt.show() -------------------------------------------------------------------------------- /Experiments/computation_time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | #################################################################### 4 | # Experiment for Figure 12 in https://arxiv.org/pdf/1901.08949.pdf # 5 | #################################################################### 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import time 10 | import cupy as cp 11 | 12 | from SRW import SubspaceRobustWasserstein 13 | from Optimization.frankwolfe import FrankWolfe 14 | from sinkhornGPU import sinkhorn_knopp_gpu 15 | 16 | 17 | def T(x,d,dim=2): 18 | assert dim <= d 19 | assert dim >= 1 20 | assert dim == int(dim) 21 | return x + 2*cp.sign(x)*cp.array(dim*[1]+(d-dim)*[0]) 22 | 23 | def fragmented_hypercube(n,d,dim): 24 | assert dim <= d 25 | assert dim >= 1 26 | assert dim == int(dim) 27 | 28 | a = (1./n) * cp.ones(n) 29 | b = (1./n) * cp.ones(n) 30 | 31 | # First measure : uniform on the hypercube 32 | X = cp.random.uniform(-1, 1, size=(n,d)) 33 | 34 | # Second measure : fragmentation 35 | Y = T(cp.random.uniform(-1, 1, size=(n,d)), d, dim) 36 | 37 | return a,b,X,Y 38 | 39 | 40 | 41 | ds = [10, 25, 50, 100, 250, 500, 1000] # Dimensions for which to compute the SRW computation time 42 | nb_ds = len(ds) 43 | n = 100 # Number of points in the measures 44 | k = 2 # Dimension parameter 45 | reg = 0.2 # Entropic regularization strengh 46 | max_iter = 1000 # Maximum number of iterations 47 | max_iter_sinkhorn = 30 # Maximum number of iterations in Sinkhorn 48 | threshold = 0.05 # Stopping threshold 49 | threshold_sinkhorn = 1e-3 # Stopping threshold in Sinkhorn 50 | nb_exp = 100 # Number of experiments 51 | 52 | 53 | times_SRW = cp.zeros((nb_exp, nb_ds)) 54 | times_W = cp.zeros((nb_exp, nb_ds)) 55 | 56 | 57 | tic = time.time() 58 | tac = time.time() 59 | for t in range(nb_exp): 60 | print(t) 61 | 62 | for ind_d in range(nb_ds): 63 | d = ds[ind_d] 64 | print(d) 65 | 66 | a,b,X,Y = fragmented_hypercube(n,d,dim=2) 67 | 68 | if d>=250: 69 | reg=0.5 70 | if d>=1000: 71 | reg=1. 72 | 73 | print('SRW') 74 | algo = FrankWolfe(reg=reg, step_size_0=None, max_iter=max_iter, max_iter_sinkhorn=max_iter_sinkhorn, threshold=threshold, threshold_sinkhorn=threshold_sinkhorn, use_gpu=True) 75 | SRW = SubspaceRobustWasserstein(X, Y, a, b, algo, k) 76 | tic = time.time() 77 | SRW.run() 78 | tac = time.time() 79 | times_SRW[t,ind_d] = tac-tic 80 | print(SRW.get_value(), len(SRW.get_minmax_values()), cp.abs(min(SRW.get_minmax_values())-SRW.get_value())/SRW.get_value()) 81 | 82 | print('W') 83 | tic = time.time() 84 | ones = cp.ones((n,n)) 85 | C = cp.diag(cp.diag(X.dot(X.T))).dot(ones) + ones.dot(cp.diag(cp.diag(Y.dot(Y.T)))) - 2*X.dot(Y.T) 86 | OT_plan = sinkhorn_knopp_gpu(a, b, C, reg, numItermax=max_iter_sinkhorn, stopThr=threshold_sinkhorn) 87 | tac = time.time() 88 | times_W[t,ind_d] = tac-tic 89 | 90 | times_SRW = cp.asnumpy(times_SRW) 91 | times_W = cp.asnumpy(times_W) 92 | 93 | times_SRW_mean = np.mean(times_SRW, axis=0) 94 | times_SRW_min = np.min(times_SRW, axis=0) 95 | times_SRW_10 = np.percentile(times_SRW, 10, axis=0) 96 | times_SRW_25 = np.percentile(times_SRW, 25, axis=0) 97 | times_SRW_75 = np.percentile(times_SRW, 75, axis=0) 98 | times_SRW_90 = np.percentile(times_SRW, 90, axis=0) 99 | times_SRW_max = np.max(times_SRW, axis=0) 100 | 101 | times_W_mean = np.mean(times_W, axis=0) 102 | times_W_min = np.min(times_W, axis=0) 103 | times_W_10 = np.percentile(times_W, 10, axis=0) 104 | times_W_25 = np.percentile(times_W, 25, axis=0) 105 | times_W_75 = np.percentile(times_W, 75, axis=0) 106 | times_W_90 = np.percentile(times_W, 90, axis=0) 107 | times_W_max = np.max(times_W, axis=0) 108 | 109 | 110 | import matplotlib.ticker as ticker 111 | plt.figure(figsize=(17,6)) 112 | 113 | mean, = plt.loglog(ds[1:], times_W_mean[1:], 'o-', lw=8, ms=20, label='Wasserstein') 114 | col = mean.get_color() 115 | plt.fill_between(ds[1:], times_W_25[1:], times_W_75[1:], facecolor=col, alpha=0.3) 116 | plt.fill_between(ds[1:], times_W_10[1:], times_W_90[1:], facecolor=col, alpha=0.2) 117 | plt.fill_between(ds[1:], times_W_min[1:], times_W_max[1:], facecolor=col, alpha=0.15) 118 | 119 | mean, = plt.loglog(ds[1:], times_SRW_mean[1:], 'o-', lw=8, ms=20, label='Subspace Robust Wasserstein') 120 | col = mean.get_color() 121 | plt.fill_between(ds[1:], times_SRW_25[1:], times_SRW_75[1:], facecolor=col, alpha=0.3) 122 | plt.fill_between(ds[1:], times_SRW_10[1:], times_SRW_90[1:], facecolor=col, alpha=0.2) 123 | plt.fill_between(ds[1:], times_SRW_min[1:], times_SRW_max[1:], facecolor=col, alpha=0.15) 124 | 125 | 126 | plt.xlabel('Dimension', fontsize=25) 127 | plt.ylabel('Execution time in seconds', fontsize=25) 128 | plt.xticks(ds[1:], fontsize=20) 129 | plt.yticks(fontsize=20) 130 | plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.0f')) 131 | plt.grid(ls=':') 132 | plt.legend(loc='best', fontsize=25) 133 | plt.show() -------------------------------------------------------------------------------- /Optimization/algorithm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | import scipy as sp 5 | import ot 6 | from Optimization.sinkhorn import sinkhorn_knopp 7 | try: 8 | from sinkhornGPU import sinkhorn_knopp_gpu 9 | import cupy as cp 10 | except: 11 | print("GPU not found.") 12 | pass 13 | 14 | class Algorithm: 15 | def __init__(self, reg, step_size_0, max_iter, threshold, max_iter_sinkhorn, threshold_sinkhorn, use_gpu, verbose=False): 16 | """ 17 | reg : Entropic regularization strength 18 | step_size_0 : Initial step size for ProjectedGradientAscent 19 | max_iter : Maximum number of iterations to be run 20 | threshold : Stopping threshold (stops when precision 'threshold' is attained or 'max_iter' iterations are run) 21 | max_iter_sinkhorn : Maximum number of iterations to be run in Sinkhorn algorithm 22 | threshold_sinlhorn : Stopping threshold for Sinkhorn Algorithm 23 | use_gpu : 'True' to use GPU, 'False' otherwise 24 | verbose : 'True' to print additional messages, 'False' otherwise 25 | """ 26 | 27 | assert reg >= 0 28 | if step_size_0 is not None: 29 | assert step_size_0 > 0 30 | assert isinstance(max_iter, int) 31 | assert max_iter > 0 32 | assert threshold > 0 33 | assert isinstance(max_iter_sinkhorn, int) 34 | assert max_iter_sinkhorn > 0 35 | assert threshold_sinkhorn > 0 36 | assert isinstance(use_gpu, bool) 37 | assert isinstance(verbose, bool) 38 | 39 | self.reg = reg 40 | self.step_size_0 = step_size_0 41 | self.max_iter = max_iter 42 | self.threshold = threshold 43 | self.max_iter_sinkhorn = max_iter_sinkhorn 44 | self.threshold_sinkhorn = threshold_sinkhorn 45 | self.use_gpu = use_gpu 46 | self.verbose = verbose 47 | self.u = None 48 | self.v = None 49 | 50 | def Vpi(self, X, Y, a, b, OT_plan): 51 | """Return the second order matrix of the displacements: sum_ij { (OT_plan)_ij (X_i-Y_j)(X_i-Y_j)^T }.""" 52 | A = X.T.dot(OT_plan).dot(Y) 53 | if self.use_gpu: 54 | return X.T.dot(cp.diag(a)).dot(X) + Y.T.dot(cp.diag(b)).dot(Y) - A - A.T 55 | else: 56 | return X.T.dot(np.diag(a)).dot(X) + Y.T.dot(np.diag(b)).dot(Y) - A - A.T 57 | 58 | def Mahalanobis(self, X, Y, Omega): 59 | """Return the matrix of Mahalanobis costs.""" 60 | n = X.shape[0] 61 | m = Y.shape[0] 62 | if self.use_gpu: 63 | ones = cp.ones((n,m)) 64 | return cp.diag(cp.diag(X.dot(Omega).dot(X.T))).dot(ones) + ones.dot(cp.diag(cp.diag(Y.dot(Omega).dot(Y.T)))) - 2*X.dot(Omega).dot(Y.T) 65 | else: 66 | ones = np.ones((n,m)) 67 | return np.diag(np.diag(X.dot(Omega).dot(X.T))).dot(ones) + ones.dot(np.diag(np.diag(Y.dot(Omega).dot(Y.T)))) - 2*X.dot(Omega).dot(Y.T) 68 | 69 | def OT(self, a, b, ground_cost, warm_u=None, warm_v=None): 70 | """Return the OT cost and plan.""" 71 | if self.reg==0.: # Solve exact OT 72 | OT_plan, log = ot.emd(a, b, ground_cost, log=True) 73 | OT_val = log['cost'] 74 | else: # Run Sinkhorn algorithm 75 | if self.use_gpu: 76 | OT_plan, log = sinkhorn_knopp_gpu(a, b, ground_cost, self.reg, numItermax=self.max_iter_sinkhorn, stopThr=self.threshold_sinkhorn, warm_u=self.u, warm_v=self.v, log=True, to_numpy=False) 77 | self.u = log['u'] 78 | self.v = log['v'] 79 | else: 80 | OT_plan, log = sinkhorn_knopp(a, b, ground_cost, self.reg, numItermax=self.max_iter_sinkhorn, stopThr=self.threshold_sinkhorn, warm_u=self.u, warm_v=self.v, log=True) 81 | self.u = log['u'] 82 | self.v = log['v'] 83 | #print(log['nb_iterations']) 84 | OT_val = np.sum(ground_cost*OT_plan) 85 | return OT_val, OT_plan 86 | 87 | def initialize(self, a, b, X, Y, Omega, k): 88 | """Initialize Omega with the projection onto the subspace spanned by top-k eigenvectors of V_pi*, where pi* (=OT_plan) is the (classical) optimal transport plan.""" 89 | if self.verbose: 90 | print('Initializing') 91 | 92 | n = X.shape[0] 93 | m = Y.shape[0] 94 | 95 | # Compute the cost matrix 96 | if self.use_gpu: 97 | ones = cp.ones((n,m)) 98 | C = cp.diag(cp.diag(X.dot(X.T))).dot(ones) + ones.dot(cp.diag(cp.diag(Y.dot(Y.T)))) - 2*X.dot(Y.T) 99 | else: 100 | ones = np.ones((n,m)) 101 | C = np.diag(np.diag(X.dot(X.T))).dot(ones) + ones.dot(np.diag(np.diag(Y.dot(Y.T)))) - 2*X.dot(Y.T) 102 | 103 | # Compute the OT plan 104 | _, OT_plan = self.OT(a, b, C) 105 | V = self.Vpi(X, Y, a, b, OT_plan) 106 | 107 | # Eigendecompose V 108 | d = V.shape[0] 109 | if self.use_gpu: 110 | _, eigenvectors = cp.linalg.eigh(V) 111 | eigenvectors = eigenvectors[:,-k:] 112 | else: 113 | _, eigenvectors = sp.linalg.eigh(V, eigvals=(d-k,d-1)) 114 | 115 | # Return the projection 116 | Omega = eigenvectors.dot(eigenvectors.T) 117 | return Omega 118 | 119 | def run(self, a, b, X, Y, Omega, k): 120 | raise NotImplementedError("Method 'run' is not implemented !") 121 | 122 | -------------------------------------------------------------------------------- /Optimization/frankwolfe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | import scipy as sp 5 | 6 | try: 7 | import cupy as cp 8 | except: 9 | pass 10 | 11 | from .algorithm import Algorithm 12 | 13 | 14 | # Algorithm 2 in https://arxiv.org/pdf/1901.08949.pdf 15 | class FrankWolfe(Algorithm): 16 | 17 | def __init__(self, reg, step_size_0, max_iter, threshold, max_iter_sinkhorn, threshold_sinkhorn, use_gpu, verbose=False): 18 | assert reg > 0 19 | step_size_0 = None 20 | super().__init__(reg, step_size_0, max_iter, threshold, max_iter_sinkhorn, threshold_sinkhorn, use_gpu, verbose) 21 | 22 | def run(self, a, b, X, Y, Omega, k): 23 | """Run the Frank-Wolfe iterations.""" 24 | maxmin_values = [] 25 | minmax_values = [] 26 | gap = self.threshold 27 | for t in range(self.max_iter): 28 | 29 | if gap < self.threshold: 30 | if self.verbose: 31 | print('Precision', gap, 'attained.') 32 | break 33 | 34 | if self.verbose: 35 | print('Iteration', t) 36 | 37 | # Optimal transport computation (Sinkhorn) 38 | C = self.Mahalanobis(X, Y, Omega) 39 | OT_val, OT_plan = self.OT(a, b, C) 40 | pi = OT_plan 41 | maxmin_values.append(OT_val) 42 | 43 | # Second-order moment of the displacements 44 | V = self.Vpi(X, Y, a, b, OT_plan) 45 | 46 | # Minimization of Linearized objective 47 | d = V.shape[0] 48 | if self.use_gpu: 49 | eigenvalues, eigenvectors = cp.linalg.eigh(V) 50 | eigenvalues = eigenvalues[-k:] 51 | eigenvectors = eigenvectors[:,-k:] 52 | else: 53 | eigenvalues, eigenvectors = sp.linalg.eigh(V, eigvals=(d-k,d-1)) 54 | 55 | Omega_hat = eigenvectors.dot(eigenvectors.T) 56 | 57 | # Frank-Wolfe step 58 | step_size = 1/(t+2) 59 | Omega = (1-step_size)*Omega + step_size*Omega_hat 60 | 61 | # Duality values 62 | sum_eigenvalues = np.sum(eigenvalues) 63 | max_maxmin_values = max(maxmin_values) 64 | gap = np.abs(sum_eigenvalues - max_maxmin_values)/max_maxmin_values 65 | minmax_values.append(sum_eigenvalues) 66 | 67 | return Omega, pi, maxmin_values, minmax_values 68 | 69 | 70 | -------------------------------------------------------------------------------- /Optimization/projectedascent.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | import scipy as sp 5 | 6 | try: 7 | import cupy as cp 8 | except: 9 | pass 10 | 11 | from .algorithm import Algorithm 12 | 13 | import sys 14 | sys.path.insert(0, "../") 15 | from utils import projection_Omega 16 | 17 | 18 | # Algorithm 1 in https://arxiv.org/pdf/1901.08949.pdf 19 | class ProjectedGradientAscent(Algorithm): 20 | 21 | def run(self, a, b, X, Y, Omega, k): 22 | """Run the ascent iterations.""" 23 | maxmin_values = [] 24 | minmax_values = [] 25 | gap = self.threshold 26 | step_size = self.step_size_0 # Fixed stepsize for Gradient Ascent 27 | for t in range(self.max_iter): 28 | 29 | if gap < self.threshold: 30 | if self.verbose: 31 | print('Precision', gap, 'attained.') 32 | break 33 | 34 | if self.verbose and t%10 == 0: 35 | print('Iteration', t) 36 | 37 | # Optimal transport computation 38 | C = self.Mahalanobis(X, Y, Omega) 39 | OT_val, OT_plan = self.OT(a, b, C) 40 | pi = OT_plan 41 | maxmin_values.append(OT_val) 42 | 43 | # Gradient step 44 | V = self.Vpi(X, Y, a, b, OT_plan) 45 | if self.reg == 0: # Supergradient Method needs diminishing stepsizes 46 | step_size = self.step_size_0/np.sqrt(t+1) 47 | Omega = projection_Omega(Omega + step_size*V, k) 48 | 49 | # Duality values 50 | d = V.shape[0] 51 | if self.use_gpu: 52 | eigenvalues = cp.linalg.eigvalsh(V) 53 | eigenvalues = eigenvalues[-k:] 54 | else: 55 | eigenvalues = sp.linalg.eigh(V, eigvals=(d-k,d-1), eigvals_only=True) 56 | 57 | sum_eigenvalues = np.sum(eigenvalues) 58 | max_maxmin_values = max(maxmin_values) 59 | gap = np.abs(sum_eigenvalues - max_maxmin_values)/max_maxmin_values 60 | minmax_values.append(sum_eigenvalues) 61 | 62 | return Omega, pi, maxmin_values, minmax_values -------------------------------------------------------------------------------- /Optimization/sinkhorn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | # This code is adapted from the package POT (https://pot.readthedocs.io/en/stable/_modules/ot/bregman.html) 6 | 7 | # Author: Remi Flamary 8 | # Nicolas Courty 9 | # 10 | # License: MIT License 11 | 12 | import numpy as np 13 | 14 | def sinkhorn_knopp(a, b, M, reg, numItermax=1000, stopThr=1e-9, warm_u=None, warm_v=None, verbose=False, log=False): 15 | """Solve the entropic regularization optimal transport problem and return the OT matrix.""" 16 | 17 | a = np.asarray(a, dtype=np.float64) 18 | b = np.asarray(b, dtype=np.float64) 19 | M = np.asarray(M, dtype=np.float64) 20 | 21 | if len(a) == 0: 22 | a = np.ones((M.shape[0],), dtype=np.float64) / M.shape[0] 23 | if len(b) == 0: 24 | b = np.ones((M.shape[1],), dtype=np.float64) / M.shape[1] 25 | 26 | # init data 27 | Nini = len(a) 28 | Nfin = len(b) 29 | 30 | if log: 31 | log = {'err': []} 32 | 33 | if warm_u is None: 34 | u = np.ones(Nini) / Nini 35 | else: 36 | u = warm_u 37 | if warm_v is None: 38 | v = np.ones(Nfin) / Nfin 39 | else: 40 | v = warm_v 41 | 42 | # Next 3 lines equivalent to K= np.exp(-M/reg), but faster to compute 43 | K = np.empty(M.shape, dtype=M.dtype) 44 | np.divide(M, -reg, out=K) 45 | np.exp(K, out=K) 46 | 47 | # print(np.min(K)) 48 | tmp2 = np.empty(b.shape, dtype=M.dtype) 49 | 50 | Kp = (1 / a).reshape(-1, 1) * K 51 | cpt = 0 52 | err = 1 53 | while (err > stopThr and cpt < numItermax): 54 | uprev = u 55 | vprev = v 56 | 57 | KtransposeU = np.dot(K.T, u) 58 | v = np.divide(b, KtransposeU) 59 | u = 1. / np.dot(Kp, v) 60 | 61 | if (np.any(KtransposeU == 0) or 62 | np.any(np.isnan(u)) or np.any(np.isnan(v)) or 63 | np.any(np.isinf(u)) or np.any(np.isinf(v))): 64 | # we have reached the machine precision 65 | # come back to previous solution and quit loop 66 | print('Warning: numerical errors at iteration', cpt) 67 | u = uprev 68 | v = vprev 69 | break 70 | if cpt % 5 == 0: 71 | # we can speed up the process by checking for the error only all 72 | # the 10th iterations 73 | # compute right marginal tmp2= (diag(u)Kdiag(v))^T1 74 | np.einsum('i,ij,j->j', u, K, v, out=tmp2) 75 | err = np.linalg.norm(tmp2 - b)**2 # violation of marginal 76 | if log: 77 | log['err'].append(err) 78 | 79 | if verbose: 80 | if cpt % 200 == 0: 81 | print( 82 | '{:5s}|{:12s}'.format('It.', 'Err') + '\n' + '-' * 19) 83 | print('{:5d}|{:8e}|'.format(cpt, err)) 84 | cpt = cpt + 1 85 | 86 | if log: 87 | log['u'] = u 88 | log['v'] = v 89 | log['nb_iterations'] = cpt 90 | 91 | if log: 92 | return u.reshape((-1, 1)) * K * v.reshape((1, -1)), log 93 | else: 94 | return u.reshape((-1, 1)) * K * v.reshape((1, -1)) -------------------------------------------------------------------------------- /Optimization/sinkhornGPU.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | # This code is adapted from the package POT (https://pot.readthedocs.io/en/stable/_modules/ot/gpu/bregman.html) 6 | 7 | # Author: Remi Flamary 8 | # Leo Gautheron 9 | # 10 | # License: MIT License 11 | 12 | 13 | import cupy as np # np used for matrix computation 14 | import cupy as cp # cp used for cupy specific operations 15 | 16 | 17 | def sinkhorn_knopp_gpu(a, b, M, reg, numItermax=1000, stopThr=1e-9, warm_u=None, warm_v=None, verbose=False, log=True, to_numpy=False): 18 | """Solve the entropic regularization optimal transport problem and return the OT matrix.""" 19 | 20 | if len(a) == 0: 21 | a = np.ones((M.shape[0],)) / M.shape[0] 22 | if len(b) == 0: 23 | b = np.ones((M.shape[1],)) / M.shape[1] 24 | 25 | # init data 26 | Nini = len(a) 27 | Nfin = len(b) 28 | 29 | if log: 30 | log = {'err': []} 31 | 32 | if warm_u is None: 33 | u = np.ones(Nini) / Nini 34 | else: 35 | u = warm_u 36 | if warm_v is None: 37 | v = np.ones(Nfin) / Nfin 38 | else: 39 | v = warm_v 40 | 41 | # Next 3 lines equivalent to K= np.exp(-M/reg), but faster to compute 42 | K = np.empty(M.shape, dtype=M.dtype) 43 | np.divide(M, -reg, out=K) 44 | np.exp(K, out=K) 45 | 46 | tmp2 = np.empty(b.shape, dtype=M.dtype) 47 | 48 | Kp = (1 / a).reshape(-1, 1) * K 49 | cpt = 0 50 | err = 1 51 | while (err > stopThr and cpt < numItermax): 52 | uprev = u 53 | vprev = v 54 | 55 | KtransposeU = np.dot(K.T, u) 56 | v = np.divide(b, KtransposeU) 57 | u = 1. / np.dot(Kp, v) 58 | 59 | if (np.any(KtransposeU == 0) or 60 | np.any(np.isnan(u)) or np.any(np.isnan(v)) or 61 | np.any(np.isinf(u)) or np.any(np.isinf(v))): 62 | # we have reached the machine precision 63 | # come back to previous solution and quit loop 64 | print('Warning: numerical errors at iteration', cpt) 65 | u = uprev 66 | v = vprev 67 | break 68 | if cpt % 5 == 0: 69 | # we can speed up the process by checking for the error only all 70 | # the 10th iterations 71 | # compute right marginal tmp2= (diag(u)Kdiag(v))^T1 72 | tmp2 = np.sum(u[:, None] * K * v[None, :], 0) 73 | #tmp2=np.einsum('i,ij,j->j', u, K, v) 74 | err = np.linalg.norm(tmp2 - b)**2 # violation of marginal 75 | #if log: 76 | # log['err'].append(err) 77 | 78 | if verbose: 79 | if cpt % 200 == 0: 80 | print( 81 | '{:5s}|{:12s}'.format('It.', 'Err') + '\n' + '-' * 19) 82 | print('{:5d}|{:8e}|'.format(cpt, err)) 83 | cpt = cpt + 1 84 | 85 | if log: 86 | log['u'] = u 87 | log['v'] = v 88 | log['nb_iterations'] = cpt 89 | 90 | res = u.reshape((-1, 1)) * K * v.reshape((1, -1)) 91 | if to_numpy: 92 | res = cp.asnumpy(res) 93 | if log: 94 | return res, log 95 | else: 96 | return res -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Subspace Robust Wasserstein 2 | 3 | This repository contains Python code for computing the Subspace Robust Wasserstein Distances defined in 4 | 5 | > Paty, F. & Cuturi, M.. (2019). Subspace Robust Wasserstein Distances. Proceedings of the 36th International Conference on Machine Learning, in PMLR 97:5072-5081 6 | 7 | A demo notebook shows how SRW distances can be computed in a simple case. 8 | 9 | The experiments from the paper can be reproduced using the code in the "Experiments" folder. 10 | -------------------------------------------------------------------------------- /SRW.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-# 2 | 3 | import numpy as np 4 | import scipy as sp 5 | import matplotlib.pyplot as plt 6 | 7 | try: 8 | import cupy as cp 9 | except: 10 | pass 11 | 12 | class SubspaceRobustWasserstein: 13 | 14 | def __init__(self, X, Y, a, b, algo, k): 15 | """ 16 | X : (number_points_1, dimension) matrix of atoms for the first measure 17 | Y : (number_points_2, dimension) matrix of atoms for the second measure 18 | a : (number_points_1,) vector of weights for the first measure 19 | b : (number_points_2,) vector of weights for the second measure 20 | algo : algorithm to compute the SRW distance (instance of class 'ProjectedGradientAscent' or 'FrankWolfe') 21 | k : dimension parameter (can be of type 'int', 'list' or 'set' in order to compute SRW for several paremeters 'k'). 22 | """ 23 | 24 | # Check shapes 25 | d = X.shape[1] 26 | n = X.shape[0] 27 | m = Y.shape[0] 28 | assert d == Y.shape[1] 29 | assert n == a.shape[0] 30 | assert m == b.shape[0] 31 | 32 | if isinstance(k, int): 33 | assert k <= d 34 | assert k == int(k) 35 | assert 1 <= k 36 | elif isinstance(k, list) or isinstance(k, set): 37 | assert len(k) > 0 38 | k = list(set(k)) 39 | k.sort(reverse=True) 40 | assert k[0] <= d 41 | assert k[-1] >= 1 42 | for l in k: 43 | assert l == int(l) 44 | else: 45 | raise TypeError("Parameter 'k' should be of type 'int' or 'list' or 'set'.") 46 | 47 | # Measures 48 | if algo.use_gpu: 49 | self.X = cp.asarray(X) 50 | self.Y = cp.asarray(Y) 51 | self.a = cp.asarray(a) 52 | self.b = cp.asarray(b) 53 | else: 54 | self.X = X 55 | self.Y = Y 56 | self.a = a 57 | self.b = b 58 | self.d = d 59 | 60 | # Algorithm 61 | self.algo = algo 62 | self.k = k 63 | if self.algo.use_gpu: 64 | self.Omega = cp.identity(self.d) 65 | else: 66 | self.Omega = np.identity(self.d) 67 | self.pi = None 68 | self.maxmin_values = [] 69 | self.minmax_values = [] 70 | 71 | def run(self): 72 | """Run algorithm algo on the data.""" 73 | if isinstance(self.k, int): 74 | self.Omega = self.algo.initialize(self.a, self.b, self.X, self.Y, self.Omega, self.k) 75 | self.Omega, self.pi, self.maxmin_values, self.minmax_values = self.algo.run(self.a, self.b, self.X, self.Y, self.Omega, self.k) 76 | elif isinstance(self.k, list): 77 | #TODO: clean this up 78 | Omega_0 = self.algo.initialize(self.a, self.b, self.X, self.Y, self.Omega, self.k[0]) 79 | self.Omega, self.pi, self.maxmin_values, self.minmax_values = {}, {}, {}, {} 80 | for l in self.k: 81 | if l != self.k[0]: 82 | Omega_0 = eigenvectors[:,-l:].dot(eigenvectors[:,-l:].T) 83 | Omega, pi, maxmin_values, minmax_values = self.algo.run(self.a, self.b, self.X, self.Y, Omega_0, l) 84 | V = self.algo.Vpi(self.X, self.Y, self.a, self.b, pi) 85 | if self.algo.use_gpu: 86 | _, eigenvectors = cp.linalg.eigh(V) 87 | else: 88 | _, eigenvectors = np.linalg.eigh(V) 89 | self.Omega[l] = Omega 90 | self.pi[l] = pi 91 | self.maxmin_values[l] = maxmin_values 92 | self.minmax_values[l] = minmax_values 93 | 94 | def get_Omega(self): 95 | return self.Omega 96 | 97 | def get_pi(self): 98 | return self.pi 99 | 100 | def get_maxmin_values(self): 101 | """Get the values of the maxmin problem along the iterations.""" 102 | return self.maxmin_values 103 | 104 | def get_minmax_values(self): 105 | """Get the values of the minmax problem along the iterations.""" 106 | return self.minmax_values 107 | 108 | def get_value(self): 109 | """Return the SRW distance.""" 110 | if isinstance(self.k, int): 111 | return np.max(self.maxmin_values) 112 | else: 113 | return {key: np.max(val) for key, val in self.maxmin_values.items()} 114 | 115 | def plot_values(self, real_value=None): 116 | """Plot values if computed for several dimension parameters 'k'.""" 117 | assert not isinstance(self.k, int) 118 | values = self.get_value() 119 | plt.plot(values.keys(), values.values(), lw=4) 120 | if real_value is not None: 121 | plt.plot(values.keys(), len(values)*[real_value]) 122 | plt.grid(ls=':') 123 | plt.xticks(np.sort(list(values.keys()))) 124 | plt.xlabel('Dimension parameter $k$', fontsize=25) 125 | plt.show() 126 | 127 | def get_projected_pushforwards(self, l=None): 128 | """Return the pushforwards.""" 129 | isnotdict = False # True iff self.Omega and other attributes are NOT dictionnaries 130 | if isinstance(self.k, int): 131 | isnotdict = True 132 | if isinstance(self.k, int) and l is None: 133 | l = self.k 134 | elif isinstance(self.k, int) and l != self.k: 135 | raise ValueError("Argument 'l' should match class attribute 'k'.") 136 | elif l is None and isinstance(self.k, list): 137 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be specified.") 138 | elif isinstance(self.k, list) and l not in self.k: 139 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be in the list 'k'.") 140 | 141 | if isnotdict: 142 | d = self.Omega.shape[0] 143 | if self.algo.use_gpu: 144 | eigenvalues, eigenvectors = cp.linalg.eigh(self.Omega) 145 | eigenvalues = eigenvalues[-self.k:] 146 | eigenvectors = eigenvectors[:,-self.k:] 147 | else: 148 | eigenvalues, eigenvectors = sp.linalg.eigh(self.Omega, eigvals=(d-self.k,d-1)) 149 | else: 150 | d = self.Omega[l].shape[0] 151 | if self.algo.use_gpu: 152 | eigenvalues, eigenvectors = cp.linalg.eigh(self.Omega[l]) 153 | eigenvalues = eigenvalues[-self.k:] 154 | eigenvectors = eigenvectors[:,-self.k:] 155 | else: 156 | eigenvalues, eigenvectors = sp.linalg.eigh(self.Omega[l], eigvals=(d-self.k,d-1)) 157 | 158 | eigenvalues[eigenvalues<0]=0. 159 | eigenvalues = np.sqrt(eigenvalues) 160 | if self.algo.use_gpu: 161 | projector = (cp.diag(eigenvalues).dot(eigenvectors.T)).T 162 | else: 163 | projector = (np.diag(eigenvalues).dot(eigenvectors.T)).T 164 | 165 | proj_X = self.X.dot(projector) 166 | proj_Y = self.Y.dot(projector) 167 | 168 | return proj_X, proj_Y 169 | 170 | def plot_projected_pushforwards(self, l=None, path=None): 171 | """Plot the pushforwards measures under Omega^(1/2).""" 172 | if isinstance(self.k, int) and l is None: 173 | l = self.k 174 | elif isinstance(self.k, int) and l != self.k: 175 | raise ValueError("Argument 'l' should match class attribute 'k'.") 176 | elif l is None and isinstance(self.k, list): 177 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be specified.") 178 | elif isinstance(self.k, list) and l not in self.k: 179 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be in the list 'k'.") 180 | 181 | proj_X, proj_Y = self.get_projected_pushforwards(l) 182 | 183 | plt.scatter(proj_X[:,0], proj_X[:,1], s=self.X.shape[0]*20*self.a, c='r', zorder=10, alpha=0.7) 184 | plt.scatter(proj_Y[:,0], proj_Y[:,1], s=self.Y.shape[0]*20*self.b, c='b', zorder=10, alpha=0.7) 185 | plt.title('Optimal projections', fontsize=25) 186 | plt.axis('equal') 187 | if path is not None: 188 | plt.savefig(path) 189 | plt.show() 190 | 191 | def plot_transport_plan(self, l=None, path=None): 192 | """Plot the transport plan.""" 193 | isnotdict = False 194 | if isinstance(self.k, int): 195 | isnotdict = True 196 | if isinstance(self.k, int) and l is None: 197 | l = self.k 198 | elif isinstance(self.k, int) and l != self.k: 199 | raise ValueError("Argument 'l' should match class attribute 'k'.") 200 | elif l is None and isinstance(self.k, list): 201 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be specified.") 202 | elif isinstance(self.k, list) and l not in self.k: 203 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be in the list 'k'.") 204 | 205 | for i in range(self.X.shape[0]): 206 | for j in range(self.Y.shape[0]): 207 | if isnotdict and self.pi[i,j] > 0.: 208 | plt.plot([self.X[i,0], self.Y[j,0]], [self.X[i,1], self.Y[j,1]], c='k', lw=30*self.pi[i,j]) 209 | elif not isnotdict and self.pi[l][i,j] > 0.: 210 | plt.plot([self.X[i,0], self.Y[j,0]], [self.X[i,1], self.Y[j,1]], c='k', lw=30*self.pi[l][i,j]) 211 | plt.scatter(self.X[:,0], self.X[:,1], s=self.X.shape[0]*20*self.a, c='r', zorder=10, alpha=0.7) 212 | plt.scatter(self.Y[:,0], self.Y[:,1], s=self.Y.shape[0]*20*self.b, c='b', zorder=10, alpha=0.7) 213 | plt.title('Optimal SRW transport plan', fontsize=25) 214 | plt.axis('equal') 215 | if path is not None: 216 | plt.savefig(path) 217 | plt.show() 218 | 219 | def plot_convergence(self, l=None, path=None): 220 | """Plot the convergence of the optimization problem.""" 221 | isnotdict = False # True iff self.Omega and other attributes are NOT dictionnaries 222 | if isinstance(self.k, int): 223 | isnotdict = True 224 | if isinstance(self.k, int) and l is None: 225 | l = self.k 226 | elif isinstance(self.k, int) and l != self.k: 227 | raise ValueError("Argument 'l' should match class attribute 'k'.") 228 | elif l is None and isinstance(self.k, list): 229 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be specified.") 230 | elif isinstance(self.k, list) and l not in self.k: 231 | raise ValueError("When class attribute 'k' is a list, argument 'l' should be in the list 'k'.") 232 | 233 | 234 | if isnotdict: 235 | plt.plot(self.minmax_values, label='Sum of the '+str(l)+' largest eigenvalues of $V_\pi$', lw=4) 236 | plt.plot(self.maxmin_values, label='Optimal transport between the pushforwards', lw=4) 237 | else: 238 | plt.plot(self.minmax_values[l], label='Sum of the '+str(l)+' largest eigenvalues of $V_\pi$', lw=4) 239 | plt.plot(self.maxmin_values[l], label='Optimal transport between the pushforwards', lw=4) 240 | plt.xlabel('Number of iterations', fontsize=25) 241 | plt.legend(fontsize=15) 242 | if path is not None: 243 | plt.savefig(path) 244 | plt.show() -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from sklearn.neighbors import KernelDensity 6 | 7 | def sample_simplex(d): 8 | '''Return one sample uniformly on the d-dimensional simplex.''' 9 | Exp = -np.log(np.random.uniform(size=d)) 10 | return Exp/np.sum(Exp) 11 | 12 | def bures_wasserstein(mean_1, mean_2, cov_1, cov_2): 13 | '''Return the OT distance between two Gaussian distributions N(mean_1,cov_1) and N(mean_1,cov_2).''' 14 | assert mean_1.shape == mean_2.shape 15 | assert cov_1.shape == cov_2.shape 16 | d = mean_1.shape[0] 17 | assert cov_2.shape == (d,d) 18 | 19 | e,v = np.linalg.eigh(cov_1) 20 | e[e<0]=0. 21 | sqrt_1 = v.dot(np.diag(np.sqrt(e))).dot(v.T) 22 | 23 | cross = sqrt_1.dot(cov_2).dot(sqrt_1) 24 | e,v = np.linalg.eigh(cross) 25 | e[e<0]=0. 26 | cross = v.dot(np.diag(np.sqrt(e))).dot(v.T) 27 | 28 | return np.linalg.norm(mean_1-mean_2)**2 + np.trace(cov_1 + cov_2 - 2*cross) 29 | 30 | def euclidean_proj_simplex(v, s=1): 31 | """ Compute the Euclidean projection on a positive simplex.""" 32 | # Adrien Gaidon - INRIA - 2011 33 | assert s > 0, "Radius s must be strictly positive (%d <= 0)" % s 34 | n, = v.shape # will raise ValueError if v is not 1-D 35 | # check if we are already on the simplex 36 | if v.sum() == s and np.alltrue(v >= 0): 37 | # best projection: itself! 38 | return v 39 | # get the array of cumulative sums of a sorted (decreasing) copy of v 40 | u = np.sort(v)[::-1] 41 | cssv = np.cumsum(u) 42 | # get the number of > 0 components of the optimal solution 43 | rho = np.nonzero(u * np.arange(1, n+1) > (cssv - s))[0][-1] 44 | # compute the Lagrange multiplier associated to the simplex constraint 45 | theta = (cssv[rho] - s) / (rho + 1.0) 46 | # compute the projection by thresholding v using theta 47 | w = (v - theta).clip(min=0) 48 | return w 49 | 50 | def projection_Omega(matrix, k, max_iter_Dykstra=10): 51 | '''Project the matrix onto {0 <= Omega <= I with Trace(Omega)=k}, using Dykstra's projection algorithm.''' 52 | d = matrix.shape[0] 53 | matrix = 0.5*(matrix + matrix.T) 54 | e,v = np.linalg.eigh(matrix) 55 | x = e 56 | p = np.zeros(d) 57 | q = np.zeros(d) 58 | for _ in range(max_iter_Dykstra): 59 | y = x + p 60 | y[y>1] = 1. 61 | p = x + p - y 62 | x = euclidean_proj_simplex(y+q, s=k) 63 | q = y + q - x 64 | x[x<0.] = 0. 65 | return v.dot(np.diag(x)).dot(v.T) --------------------------------------------------------------------------------