Repository: giotto-ai/giotto-tda (language: python)

Issue:
Changes in git history
Please be aware that the final moving from gitlab to github has led us to modify git history. 

@rth, could you please refork?


Discussion / hints:
Sure, will do. Thanks @gtauzin !

Fix patch:
diff --git a/doc/modules/index.rst b/doc/modules/index.rst
index 899224c6b..4896b2863 100644
--- a/doc/modules/index.rst
+++ b/doc/modules/index.rst
@@ -10,6 +10,7 @@ This pages contains a list of available features in the library.
 
    mapper.rst
    homology.rst
+   local_homology.rst
    diagrams.rst
    curves.rst
    point_clouds.rst
diff --git a/doc/modules/local_homology.rst b/doc/modules/local_homology.rst
new file mode 100644
index 000000000..a1a34ae69
--- /dev/null
+++ b/doc/modules/local_homology.rst
@@ -0,0 +1,17 @@
+:mod:`gtda.local_homology`: Persistent local homology
+=====================================================
+
+.. automodule:: gtda.local_homology
+   :no-members:
+   :no-inherited-members:
+
+Local simplicial homology
+-------------------------
+.. currentmodule:: gtda
+
+.. autosummary::
+   :toctree: generated/local_homology/
+   :template: class.rst
+
+   local_homology.KNeighborsLocalVietorisRips
+   local_homology.RadiusLocalVietorisRips
diff --git a/doc/notebooks/examples.rst b/doc/notebooks/examples.rst
index 39e655cda..c34a33240 100644
--- a/doc/notebooks/examples.rst
+++ b/doc/notebooks/examples.rst
@@ -11,6 +11,7 @@ This page contains examples of use of ``giotto-tda``.
    classifying_shapes
    lorenz_attractor
    MNIST_classification
+   local_hom_NLP_disambiguation
    voids_on_the_plane
 
 ..
diff --git a/doc/notebooks/tutorials.rst b/doc/notebooks/tutorials.rst
index 2d01b1f49..64deb324f 100644
--- a/doc/notebooks/tutorials.rst
+++ b/doc/notebooks/tutorials.rst
@@ -14,6 +14,7 @@ by Lewis Tunstall provides a friendly introduction to the philosophy of ``giotto
    topology_time_series
    time_series_forecasting
    persistent_homology_graphs
+   local_homology
 
 ..
    include:: vietoris_rips_quickstart.rst
diff --git a/examples/data/note.n.xml b/examples/data/note.n.xml
new file mode 100644
index 000000000..6f73557f2
--- /dev/null
+++ b/examples/data/note.n.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<note.n.train><note.n.1>One of the properties is a print history. This will detail the most recent printing activity concerning the file ( although it only records the printing requests , it is not absolute proof that a print was actually produced ) . Note that it only allows a maximum of 250 characters and may therefore not provide a complete history; in the event of the history exceeding this volume , it can only guarantee details of the most recent activity . </note.n.1><note.n.2>Lacking absolute pitch , most of us ca n't make that connection-labelling a note as " D " , for example. But do the connections and labels get hammered in during music lessons , or are some babies just born with a flair for identifying pitch ? That 's a hard question to answer , since musical parents often pass a passion for music-as well as their genes-on to their children . </note.n.2><note.n.3>From what I read in this thread , I gather that many SSPX faithful are frustrated by this note from the Secretariat of State. They expected the Vatican to say nothing more , and interpret the note as a bow to pressure . </note.n.3><note.n.4>Part of the collection of images from Great Smoky Mountains National Park. These selections are from almost 30 years of photography in the park. Richard began shooting early on in his career here , mostly in black &amp;white. Later he began shooting more and more of his signature color work. We are pleased to be able to offer some of these exquisite images as note cards - for what better way to stay in touch ! </note.n.4><note.n.5>It is called the Tartini tone. It is most often noticeable when the two notes being played are sustained , about equally loud , not too low in pitch ( say treble clef and above ) and moderately loud. I 've put a section about it on a page called Interference beats and Tartini tones where you can hear sound files made with pure sine waves that show the effect quite well. If you listen for it , you can use it to tune your chords : the tuning you will arrive at is Just Intonation , and not Equal Temperament. In sustained chords , the former usually sounds better . </note.n.5><note.n.6>described in the Constitution for the united States of America , and the other officers and agents of government do not immediately bring him or her under control , then the breach of contract with the public makes the official , the accessory officers and agents of the official of the government , and the government itself , all liable to the public for the damage specified in the U.S. Code , and the damage must be rebated back to the public from the U.S. Treasury out of the taxes paid into the government by the public , by the use of a Public Contract Tax Rebate Note , </note.n.6><note.n.7>Voicing statements specifies the harmonic continuity of the music and control the voice leading between chords. The entry of a particular voicing position specifies the structure note of the next chord which the note in the current structure component must move to. An example is </note.n.7><note.n.8>... I thought it best that I quote the following passage in full : ( Please note that Richard Dearlove , the head of MI6 - CIA equivalent - is the one referred to as C . ) </note.n.8><note.n.9>Bowen argued that the notes had " serious flaws. " He was disturbed that the adviser described him as embracing the " New Historian " narrative; insisted that Martin Gilbert and Michael Oren should not be seen as " mainstream , " apparently due to their " support of the Jewish state " and Zionism; and cast the controversy about the New Historians ' revisionist historiography as being driven by " right wingers. " </note.n.9><note.n.10>The US government -- enh , the Federal Reserve -- does not promise to redeem your money with gold. Instead , the notes are backed , ultimately , by the full faith and credit of the US government.'' I think this means that they 'll redeem your worn-out dollars for crisp new ones. Sounds kinda wishy-washy , but it seems to work. To be fair , it 's a little more magical than that. A dollar is worth a dollar because Congress says it is legal tender : if a debt is denominated in dollars , then federal reserve bank notes have to be accepted in payment of it. It 's really beautiful when you think of -- an entire economy held together by an abstract set of mutual obligations to honor a money concept only symbolically and rather incompletely backed by anything of value.'' Of course , the value of the dollar in terms of anything other than dollars is variable . </note.n.10><note.n.11>Can anyone in a serious , straightforward way write an intellectually honest paragraph to back that up ? In other words , can anyone make the case that John Kerry has thrown caution to the wind in any meaningful sense to fight for " what 's right " ? I follow politics fairly closely and I 've been doing my reading about John Kerry but I have n't a clue as to how he 's talking about his own political career. Note : if you 're going to try , you should know that I do n't consider the case against Bush to be the case for Kerry in this regard. This is about how Kerry has conducted himself since Vietnam. I just want to know how a Kerry supporter could honestly take this statement at face value . </note.n.11><note.n.12>Document 13 : CIA , Draft Presidential Finding , " Scope : Hostage Rescue - Middle East , " ( with cover note from William J. Casey ) , November 26 , 1985 </note.n.12><note.n.13>Usually , we have reason to consider only some of the possible ways of analyzing a given set of notes into lines. We may , however , wish to consider different parsings of the same notes for different purposes. Lines , in fact , can be of a number of different types. As Westergaard says ( ITT , p. 289 ) : </note.n.13><note.n.14>" I do n't believe that most of the agents that sold this stuff knew it was a sham company , " said Dave Sutton , assistant statewide prosecutor. " That 's the reason they should n't have been out there selling it. They did n't see audited financial statements. They did n't know whether the insurance company guaranteeing the notes was legitimate. " </note.n.14><note.n.15>n194. See generally Cost of Credit , supra note 22 , 10.3.2.1.1 , at 339 ( discussing retail installment sales acts in historical context ); Curran , supra note 196 , at 13 . </note.n.15><note.n.16>The systems contain all our musical data. Each system is a group of related staves , normally bracketed together and separated vertically from other systems. Each staff contains the actual notes and whatnot. If no system is given explicitly , one is created automatically. Similarly , if no staff is given , one is provided automatically. Here 's an example of a score with two systems , one of which has two staves : </note.n.16><note.n.17>Scoop : Beginning a note beneath it 's pitch , then sliding up to the correct pitch. Scooping was the prominent feature of " crooners " in the 1920s-50s; Bing Crosby , Frank Sinatra , and Perry Como were among the singers famous for this style. Today , scooping should be used infrequently and only to achieve a specific quality or emotion . </note.n.17><note.n.18>Use of labels. Labels and label paper ( adhesive-backed paper ) are used for a variety of reasons including patient demographics , transcription of dictated progress notes , printing of physician orders for telephone orders , medication , or treatment records. When labels are used in the record , a number of issues or concerns must be considered and addressed before implementation. Organization policies and practices should address how and where labels will be placed. Information may not be obscured by the label , and the adhesiveness of the label must be adequate for the retention period of the document . </note.n.18><note.n.19>183. See supra notes 43-47 and accompanying text ( describing the factors to consider ) . In the instant case , Slovakia certainly should have appraised the following factors : 1 ) the physical extent of the Danube River and interconnected groundwater aquifers lying within the territory of Hungary; 2 ) the relationship and dependency of the region 's environment upon the river and aquifers and the impact the scheme would have on the flora and fauna and ecology of the region; 3 ) Hungary 's reliance on the water resources for agricultural needs and potable water supplies; 4 ) the means for conserving and protecting water quality and quantity; and 5 ) possible alternatives to the planned project. Id . </note.n.19><note.n.20>The category or categories of headings after which a free-floating heading can be used are encoded in the 073 field of subject subdivision ( 180 and 185 ) authority records. $ a contains the section of the Subject Cataloging Manual that discusses that type of heading , such as H 1100 , and $ z contains lcsh. If more than one section applies , each section is given in a separate $ a. The instructions for use are also given as notes in 680 $ i in the form Use as a topical subdivision under classes of persons. </note.n.20><note.n.21>The Bank of England was purported to be designed to protect the public by preventing fraud and economic chaos , yet this is exactly what it caused by enabling private banks to engage in fractional reserve banking. In less than three years after its inception , the bank was unable to meet depositor demand to redeem bank notes with precious metal , but it did not matter because the government simply declared that the bank was not required to do so . </note.n.21><note.n.22>Bank Notes are a horse of another color , because they always draw interest. ( Federal Reserve Notes are not bank notes. ) However , this is only the superficial difference from United States Notes. Federal Reserve Notes , series 1963 , 1969 , 1974 are not redeemable into specie , as the prior series were , nor into anything but more of the same. Surely that has a meaning beyond the obvious one. The only other thing that these Notes are convertible into is United States government bonds. So , in effect , the Notes are merely " small change " for the bonds. They are the " till money " used by the banks to balance up the demand deposit accounts at the end of the day. If you have a large enough bundle of Federal Reserve Notes you can convert them into a bond by simply purchasing the bond , but never into specie , as that would defeat the scheme of liability on credit for filing an income tax return levied not at law but in equity , and executive equity at that . </note.n.22><note.n.23>Only the person who owes the money needs to sign it. There is no requirement that the note be witnessed or notarized. Thomas Moens </note.n.23><note.n.24>[ CB-547-548 , note 2 ] Some courts admit expert testimony concerning the trauma suffered by victims of rape or child abuse , and such testimony sometimes has the effect of bolstering credibility. See Notes on Proving Truthfulness ( Chapter 8C2 , infra ) . </note.n.24><note.n.25>When spelling the other Major scales , like G Major , D Major , F Major , etc. , sharped or flatted notes are needed to keep that half-step spacing between the 3-4 and 7-8 scale degrees. That interval spacing is what gives us the Major scale , and its particular sound. No big mystery . </note.n.25><note.n.26>The following table details the frequency and note value for each musical note in a single octave. Double the values to raise the frequency and note values by one octave. Divide the values by two to reduce the frequency and note values by one octave. For example , C in the next higher octave is 523.2 Hz , while C in the next lower octave is 130.8 Hz. Note that C4 ( Middle C ) and SILENCE have both been defined as constants . </note.n.26><note.n.27>Q. What is a demand promissory note ? A. The balance owing in a demand promissory note does not need to be paid until the Lender demands to be repaid. In other words , the loan is repayable 'on demand'. There is no fixed end date for the repayment of the note. Upon demand , the Borrower is given a certain period of time to repay the outstanding balance of the note . </note.n.27><note.n.28>ENTERING NOTES : See Exhibit 2.11.1-21. is an example of possible notes. A total of 70 lines are allowed for notes. " [ ] " should be on the very last line of the workfile. If there are no notes for this record , then the ending brackets should be on line 49. It is not necessary to enter asterisks or dotted lines between notes as program CRX09 will provide them . </note.n.28><note.n.29>The datatype " string " is defined in " XML Schema Part 2 : Datatypes " Specification [ 11 ] . Note that this is not identical to the type called " string " in many database or programming languages , and in particular may forbid some characters those languages would permit. ( Those values must be represented by using some datatype other than xsd : string . ) </note.n.29><note.n.30>Once you transfer a certain amount of private IOU debt obligations you are subject to the liability that comes from using the private scrip in the trade or business of the United States. Why are they debt obligations ? Now you see why they are first liens on all property that you think you own , but do not. This comes directly from the Federal Reserve page itself where they say the notes are worthless. Here are a few excerpts : </note.n.30><note.n.31>Death Note ( Desu Nto ) is a Japanese manga series created by writer Tsugumi Ohba and illustrator Takeshi Obata. The series centers around a high school student who discovers a supernatural notebook that allows him to kill anyone by writing the victim 's name and picturing his face. The plot follows his attempt to create and lead a world cleansed of evil using the book , and the complex conflict between himself and those assailing him that results . </note.n.31><note.n.32>- thus obligating all parties to accept them as payment-in-full for contracted debt. While this debate was on-going the cash needs of the government called and the Act of February 12 , 1862 authorized an additional $ 10 , 000 , 000 in Demand Notes. This act brought the final possible amount of Demand Notes that could be issued to a sum of $ 60 , 000 , 000 ( by April the full $ 60 , 000 , 000 in Demand Notes had been issued ) . </note.n.32><note.n.33>3. Using Jonathan Bennett 's " Berkeley and God " as a critical foil , critically consider Berkeley 's arguments for a deity. Bennett 's essay is on reserve in the Library in Locke and Berkeley , eds. Martin and Armstrong ( Notre Dame : Univ of Notre Dame , 1968 ) , pp. 380-399 ( available from the Reserve Desk in the Library ) . In writing your paper , indicate what Bennett has to say about Berkeley 's arguments for God. In addition to discussing Bennett , of course , you will have to clarify Berkeley 's views. Critically evaluate the views you discuss. Note : you may find E.J. Furlong 's " Berkeley and the Tree in the Quad " which is in the same volume to be helpful . </note.n.33><note.n.34>Installments , or payment ( if single payment Note ) , not paid within 5 days of due date shall incur a fee of 15 of the installment or payment , but not less than $ 10.00. The Holder shall be entitled to all costs of collection should this Note , or any part of the indebtedness evidenced hereby , be accelerated and not paid. Should this note be collected at law or by an attorney at law , an Attorney/Collection Fee of 15 of the balance due , but not less than $ 100.00 , plus any costs and administration fees , shall be added . </note.n.34><note.n.35>Specifies the note transposition table to be used for source pattern transposition : " Bypass " , " Melody " , " Chord " , " Bass " ( Replaced by " Melody " in newer models ) , " Melodic Minor " , " Melodic Minor 5th Var. " , " Harmonic Minor " , " Harmonic Minor 5th Var. " , " Natural Minor " , " Natural Minor 5th Var. " , " Dorian " or " Dorian 5th Var. " </note.n.35><note.n.36>Each Noney note has the same denomination : zero. This doesn ' t mean each note has no value just relative value. There ' s no fixed exchange rate or location of operation. Noney ' s worth as both art and currency is something to negotiate through each individual transaction - anywhere . </note.n.36><note.n.37>Arrays are structures pitch class sets where each PC is a member of at least two dimensions at the same time. Two-dimensional arrays are written out as boxes in which the horizontal dimension represents " voices " and the vertical dimension " chords " . The designation of a two-dimensional array gives the number of notes in the voices followed by " x " and the number of notes in the chords ( i.e. , a 5 x 4 array ) . Arrays of a higher number of dimensions include sets of arrays of a smaller number of dimensions. In my system , I only consider higher numbers of sets of the same sizes : three-dimensional arrays consist of two-dimensional arrays of the same dimensions ( i.e. , four 5 x 4 arrays ) , and four-dimensional arrays sets of three-dimensional arrays ( i.e. four 4 x 5 x 4 arrays; in practice things rarely get this heavy ) . </note.n.37><note.n.38>The MIDI Monitor Filter window is very useful for focussing on specific types of MIDI events. You can set it to show input/output. You can specify which MIDI channels you want shown , which MIDI Events ( for example , Note On , Control Change , Program Change , MIDI Timing Clock , etc. ) , and which Controllers ( for example , Volume [ 7 ] , Pan [ 10 ] , etc. ) . It is easy to set up ANY combination of events you want to view . </note.n.38><note.n.39>Another method of adding messages to the folder is via the Pine composer 's Fcc : field. If you are sending a message that you expect to send in the same form again , you can enter the Form Letter Folder 's name in this field. Pine , as usual , will copy the message as it 's sent. Note , when you later select this message from your Form Letter Folder , it will have the same recipients as the original message . </note.n.39><note.n.40>Khaled ( formerly known as Cheb Khaled ) , the undisputed King of Rai , became internationally famous with the release of " Didi " ( Track 1 , Side A ) . ( Although it was recorded in Los Angeles , under the direction of U.S. pop producer Don Was , it was not as big a hit in the U.S. as it was in other countries. ) Rai has long been an eclectic genre , drawing on influences such as flamenco , Moroccan Gnawa music ( see notes below under Morocco ) , French cabaret , reggae , and Western pop influences. " Didi " opens with a darabuka solo that accompanies Khaled , who sings in Arabic. This introduction soon </note.n.40><note.n.41>Red-seal United States Notes represent labor value , meaning that they are backed by the positive value of the labor of American citizens actually performed in the past , and paid into the United States Treasury as U. S. taxes . </note.n.41><note.n.42>D &amp;B sticks to some basics and often thrives on complete repetition. One wonders if it is intentionally so in order to produce a more trance like state. 45 to 72 beats per minute are said to produce this , and evens a shaman said something around 60 bpm was used for lucid dreaming. This of course applies to trance music too but often it does n't keep up the repetitive beat. Despite it 's name drum and bass it has many layers on top of the smooth bass line. Drum and bass can use the same types of things as techno but also has more pad or string sounds using drawn out notes in the background . </note.n.42><note.n.43>The second main area for tweaking is the Control Track , which is found at the bottom of the screen ( see Fig. 1 , bottom ) . This area provides access to a large number of parameters , such as Velocity and Pitch Bend , but also to other , less-traditional note-shaping functions. The most familiar of these is a set of four resonance filters , each with a frequency , bandwidth , and gain control. As with other parameters , frequency values are in relative increments ( 0 to 127 ) , with no indication of actual Hertz values. You enter values using the same three data-entry tools found in the Vibrato dialog ( Dot , Free , and Line ) , and if you 've ever drawn a controller curve for notes in a sequencer , you 'll get the process immediately . </note.n.43><note.n.44>Lieberman Forum ( Rochford testimony ) , supra note 30 , at 19. This view is disputed. Others see little sign that user costs have declined in the AFS marketplace , even as it has grown. RTO and check cashing costs have not declined noticeably despite growth. See supra note 29 as to the rise in the cost of cashing a social security check even as competition increased. See also Lieberman Forum ( Fox testimony ) , supra note 121 , at 7 ( explaining why competition between lenders will not provide consumers with lower prices ) . It is critical to look at the actual track record before taking this rationale on faith in the AFS marketplace . </note.n.44><note.n.45>( 330. ) See , e.g. , To the Freemen of Pennsylvania , supra note 305 , at 365 ( " See , in Rhode-Island , the bonds of society and the obligations of morality dissolved by paper money and tender laws. " ); see also PA. HERALD ( Philadelphia ) , June 9 , 1787 , reprinted in 13 DOCUMENTARY HISTORY , infra note 344 , at 132 ( reporting incorrectly that the Constitutional Convention had " resolved that Rhode-Island should be considered as having virtually withdrawn herself from the union ... . [ S ] he shall be compelled to be responsible ... . " ) . </note.n.45><note.n.46>Solf ge is a way of assigning syllables to names of the musical scale. In order , they are today : Do , Re , Mi , Fa , Sol , La , Ti , and Do ( for the octave ) . The classic variation is : Do , Re , Mi , Fa , Sol , La , Si , Do. These functional names of the musical notes were introduced by Guido of Arezzo ( c.991 after 1033 ) using the beginning syllables of the first six musical lines of the Latin hymn Ut queant laxis. The original sequence was Ut , Re , Mi , Fa , Sol , La , where each verse would start a note higher. " Ut " later became " Do " . The equivalent syllables used in Indian music are : Sa , Ri , Ga , Ma , Pa , Dha , and Ni , while the 'bilinear music notation ' system offers a chromatic method : Li , ( Je ) , Ja , ( Bo ) , Baw , Zu , ( Zer or Fer ) , Fee , ( De ) , Da , ( Go ) , and Gaw. See also : solf ge , sargam , Kod ly Hand Signs. In China Qi is used instead of Ti ( Qi for , Chinese 7 ) . </note.n.46><note.n.47>This application note describes the operation of high-side Nchannel MOSFET switch drivers designed specifically for operation in battery-powered equipment , such as notebook and palmtop computers and portable medical instruments. A selection guide simplifies the proper choice of MOSFET and driver for a particular high-side switch application. Circuits to drive and protect load impedances ranging from large inductors to large capacitors are described and a section on surface mount and copper clad shunts is included . </note.n.47><note.n.48>During the period from 1863 to 1929 , the Government again permitted thousands of banks to issue their own notes under the National Banks Acts of 1863 and 1864. These were called " National Bank Notes , " but unlike the earlier " State Bank Notes , " they were produced on paper authorized by the U.S. Government and carried the same basic design . </note.n.48><note.n.49>Set those aside and drill a tiny hole in the flange of your NEW 195* thermostat to help bleed out air from the head. NOTE ! DO NOT USE A NAIL OR PUNCH TO DO THIS ! Punching a hole risks deforming the thermostat and impairing it 's ability to function. Be aware that some thermostats will already have this hole predrilled and you may be able to skip this step . </note.n.49><note.n.50>b. Harper investigated defendant 's claim , and discovered that the three $ 5 , 000.00 notes had been paid off prior to the December 14 , 1983 , transaction . </note.n.50><note.n.51>During 1814 federal finances deteriorated as the war dragged on , and banks outside New England suspended specie payment on August 31 , 1814. The value of the Treasury Notes fell below that of specie. New England states were unsympathetic to the war and when the government attempted to withdraw deposits from a Boston bank to make interest payments on October 1 , 1814 , the bank took the position that it could tender Treasury Notes to the government which were then rejected by the holders of the government bonds who expected payment in specie. These developments led to changes in the final Treasury Note act of the era signed on February 24 , 1815. These last notes were divided into large ( $ 100 and over ) and small ( under $ 100 ) denominations , and did not expire at any predetermined time. The large notes paid interest as before , at 5 2/5 per annum , but could also be used to purchase 6 percent interest bonds at par ( i.e. were fundable into the bonds ) as a way of supporting their value . </note.n.51><note.n.52>Special Note : A few users have had troubles with the drivers that come with this device and Andrea has posted updated drivers for the Pure Audio series of products at this location : Software Update for USB Audio Products. Scroll to the bottom of the page . </note.n.52><note.n.53>A side note : As speech communication with computers evolves , we 'll find that the machines can listen faster than we talk. Power users will learn to speak ( and listen ) faster with a varied diction , including faster consonants and click sounds. The development of a computer dialect seems like something out of science fiction , but it is actually no more far-fetched than watching a fast typist . </note.n.53><note.n.54>If this music video doesn ' t slap a huge grin on your face , then you have no soul ..There , I said it. The Sour Notes have been making some noise in Austin for some time now and are finally getting some much deserved recognition. They ' ll be playing a ridiculous amount of shows SXSW ( 6 shows ! ) while currently working on their 5th album , Write What You Know , expected to release later this year. Here ' s Do-ers &amp;Say-ers from their latest , It ' s Not Gon na Be Pretty . </note.n.54><note.n.55>Date and time when the file was written by the tool. Except the current document name , none of these values can be changed from within the tool. Note that by default the name of the document is equal to the file name when it is saved. When a document file is loaded and it contains a document name that is different from the file name ( because the file had been renamed ) , a question dialog is raised which gives the user the choice between these two names for the new document name . </note.n.55><note.n.56>How is the rhythmic pattern notated ? We have seen the oval-shaped symbols ( notes ) which are used to notate pitches. These same notes are also used to indicate rhythmic values by the use of certain alterations : </note.n.56><note.n.57>The promissory note below can be used as a sample for you to write one that fits your personal needs. Just COPY and PASTE to your word processor. NO NEED TO DOWNLOAD . </note.n.57><note.n.58>Numbers are written on the lines to show you where to fret the string with the left hand. If a zero appears , this means play the open string. Like standard musical notation , you read from left to right to find out what order to play the notes. The following piece of TAB would mean play the sequence of notes ( E F F G G A ) on the bottom E string by moving up a fret at a time , starting with the open string . </note.n.58><note.n.59>41. - 7. When a distress will be a waiver of a forfeiture of the lease. On this subject , see 1 B. &amp;Adol. 428. The right of distress , it seems , does not exist in the New England states. 4 Dane 's Ab. 126; 7 Pick. R. 105; 3 Griff. Reg 404; 4 Griff. Reg. 1143; Aik. Dig. 357 , nor in Alabama , Mississippi , North Carolina , nor Ohio; and in Kentucky , the right is limited to a distress for a pecuniary rent. 1 Hill. Ab. 156. Vide , generally , Bouv. Inst. Index , h . t.; Gilb. on Distr. by Hunt; Bradb. on Distr.; Com. Dig. h. t.; Bac. Ab. h. t.; Vin. Ab. h. t.; 2 Saund. Index , h. t.; Wilk. on Repl.; 3 Chit. Bl. Com. 6 , note; Crabb on R. P. 222 to 250 . </note.n.59><note.n.60>The governing law is the law of the jurisdiction in which the promissory note will be entered into. Often the parties select the jurisdiction where the Lender resides. If the promissory note relates to the purchase of certain assets , then the location of those assets is selected . </note.n.60><note.n.61>First , the one won note represented about l and l/3 months pay for the North Korean Army private. If he was found to be carrying one won , it would be cause for suspicion. The first message reaching the target audience would be one of disappointment. Half the leaflets could be expected to land with the money side up. The " finder " would think he had one won , only to be disappointed when he discovered the ROK flag on the other side . </note.n.61><note.n.62>'The Fed Note is essentially unsound. It is the worst currency and themost dangerous that this Country has ever known. When the proponents of the actsaw that the Democratic doctrine would not permit them to let the proposedbanks issue the new currency as bank notes , they should have stopped at that.They should not have foisted that kind of currency , namely , an asset currency , on the United States Government. They should not have made the Government [ liable on the private ] debts of individuals and corporations , and , least ofall , on the private debts of foreigners. 'As Kemerer says : 'The Fed Notes , therefore , in form , have some of the qualities of Government paper money , butin substance , are almost a pure asset currency possessing a Governmentguarantee against which contingency the Government has made no provisionwhatever . ' </note.n.62><note.n.63>Finally , structure your message to leave room for hope. Even when conveying bad news , include some positive note that allows your audience to continue listening. For example , if the company is suffering a downturn , project a note of optimism about the benefits the changes ahead might bring . </note.n.63><note.n.64>88. In contrast , in one incident , credit card information belonging to more than 20 , 000 customers that had been stored in an insecure database was compromised. See Jonathan Littman , The Fugitive Game 325 , 348 ( 1996 ) ( reporting apparent copying of credit card records by Kevin Mitnick ) . Back to text at note 88 </note.n.64><note.n.65>Text can be attached to any printable symbol , except for some global symbols ( such as a clef or instrument number ) . When attaching text to a note no comma is used , since the text is regarded as a part of the note structure. However , for all other symbols , one must insert a comma between the symbol and the text. In The Note Processor the default base line of the text syllable is -6. This may be altered by an explicit space code with global effect , e.g. , -10 ! . </note.n.65><note.n.66>Then , more recently , I was reading the various articles on the Noteworthy website about pitch bend and the penny finally dropped. Understand , I was always aware of the pitch bend wheel on my MIDI keyboard , I just had n't realised that it was something that Noteworthy provided a facility to control. So I started to do some number crunching , and I have come up with the required pitch bend setting for each note , to give you Just Intonation for any given keynote . </note.n.66><note.n.67>In keyboard music written for non-sustaining instruments like the piano and harpsichord , or plucked instruments like guitar , the chorale style does not work as well , since the sound dies away quickly. Music written for these instruments ( and also for larger ensembles ) often uses a technique called arpeggiation , where one voice or instrument plays the individual notes of the chord in succession , rather than all at once. When played rapidly enough , they are still perceived as all part of the same chord. Listen to the following two examples of the same chords , one in chorale style , the other in an arpeggiated style : </note.n.67><note.n.68>Banker ' s Note. A commercial instrument resembling a bank note ( a promissory note issued by a bank intended to circulate as money ) in every particular except that it is given by a private banker or unincorporated banking institution . </note.n.68><note.n.69>1995 collection with a lovely introduction and biography. Also , very helpful notes about each piece in particular with suggestions by group members. Quite the variety of material , from a two Beatles songs , Queen 's " Seaside Rendezvous , " traditional folk songs such as the Japanese " Takeda Lullaby , " Renaissance pieces such as " If Ye Love Me " by Tallis and so on. Mostly four voices , but with an SSATB version of " Sing And We Chant It. " </note.n.69><note.n.70>A technique whereby the notes are slurred directly from one to another , producing a continuous rise or fall in pitch. When performed on the piano , the pianist plays all the white notes or all the black notes together in rapid scales . </note.n.70><note.n.71>chromaticism , chromatic Use of notes not in the basic scale of a composition or passage. Harmonic style frequently using such notes . </note.n.71><note.n.72>Ten dollar Demand Notes feature a portrait of Abraham Lincoln at left and an allegorical figure representing art to the right. In the top center of the note is a vignette of a Bald Eagle perched on olive branches with a ribbon stating E PLURIBUS UNUM. Next to the Bald Eagle is a heraldic stars and stripes shield. Both the portrait of Lincoln and Bald Eagle vignette were stock elements used on previous banknotes issued by the American Bank Note Company . </note.n.72><note.n.73>37. Refer to the sources cited in note 35 above. See also Charles Jordan and Norman Wilson interviews , both of NAR , 2 Mar. 1971. An executive at North American who reviewed a draft of the manuscript maintained that over a period of time , the NASA welding concepts were not appreciably superior to North American techniques. Barton to author , with attachment , 18 June 1976 . </note.n.73><note.n.74>Another characteristic of a Harmonic tega-chord is the " low note " , which appears in the third tetra-chord , in the second note. The same note E appears twice in the scale but only in the higher register changes to a " low note " </note.n.74><note.n.75>B. A contingent liability is a potential liability that exists on a discounted note from the discount date until the maturity date. The contingency is not reported on the balance sheet , but rather in the footnotes . </note.n.75><note.n.76>The first three notes of the scale ( Do , Re , Me ) beginning with the note " C " are : " C " , " D " , and " E " . The second note , " D " , is 9/8 times the frequency of the first note , and the third note , " E " , is 5/4 times the frequency of the first. So , the frequencies of these three notes in the " C " scale are : </note.n.76><note.n.77>Michigan Lighthouses Note Pad - 5 1/2 " by 8 " writing pad. Full color cover features a collage of Michigan lighthouses and folds from the top. Inner pages tear off from the top , with each lined page featuring full color photographs of Whitefish Point , Copper Harbor , Grand Traverse , Point Betsie and Presque Isle lighthouses. MIP32 $ 5.00 </note.n.77><note.n.78>Please contact your network service provider. They will be able to help you with this. Please note that in most parts of the world you need to pay money to get a domain . </note.n.78><note.n.79>In this note , the State 's deposits and investments are classified in categories of " custodial credit risk " . This is the risk that the State will not be able ( a ) to recover deposits if the depository bank fails or ( b ) to recover the value of investments or collateral securities that are in the custody of an outside party if the counterparty to the investment or deposit transaction fails. Classification in category 1 means that the exposure of deposits or investments to potential custodial credit risk is low. The level of potential custodial credit risk is higher for those deposits or investments classified in category 2 , and highest for those in category 3 . </note.n.79><note.n.80>A. We work out a monthly amount that each client can afford to pay. That amount is electronically debited from their personal bank account and deposited into their " settlement account " in a trust account through Note World on the day of the month you choose . </note.n.80><note.n.81>Among the several issues of Treasury Notes , of special note are the " Small Treasury Notes " of 1815 which , like the Demand Notes , were intended to circulate as currency. However only $ 3 , 392 , 994 were issued , and these were rapidly exchanged for bonds. In witness to the limited circulation achieved by these notes , only two issued uncancelled examples of the Small Treasury Notes are known today . </note.n.81><note.n.82>&gt; " I Love Belgian Sheepdogs " Note Pad " I Love Belgian Sheepdogs " Note Pad These " I Love Belgian Sheepdogs " Note Pads are a great gift idea. The note pads are printed on 20 lb White paper , 100 sheets each , and have a cardboard backing with glued top edge. A clear storage bag with resealable adhesive strip is also included. Model : NP-DOG119b </note.n.82><note.n.83>132. See U.C.C. Sec. 2-102 ( Unless the context otherwise requires , this Article applies to transactions in goods . . . . ) . Proposed revisions to Article 2 may extend its coverage to include service contracts. See Raymond T. Nimmer , Intangible Contracts : Thoughts of Hubs , Spokes , and Reinvigorating Article 2 , 35 Wm. &amp;Mary L. Rev. 1337 , 1374 , 1389 ( 1994 ) . This change would greatly increase the likelihood that Article 2 applies to the provision of a certificate. Back to text at note 132 </note.n.83><note.n.84>Comments are created in the dedicated ' Comments ' tab , seen to the right of the ' Conversation ' tab. Much like sticky notes , jot down whatever information you want and save. All comments will appear in this tab in reverse chronological order , but are also shown in the ticket ' Conversation ' tab as well. They will filter in chronologically with the appropriate flow of the surrounding messages , whether it ' s newest to oldest or oldest to newest ( read all ) . </note.n.84><note.n.85>Given the preceding analyses a skeptical reader might conjecture that the introduction of any note would have a similar effect of reducing uncertainty -- and so produce positively valenced prediction and tension responses. As a control case , consider the concocted passage shown in Figure 33. This example shows a dominant-tonic progression with an " odd-ball " note interposed. A brief analysis follows . </note.n.85><note.n.86>Please Note : The National Resource Inventory ( NRI ) program acquires aerial photos of small 1/4 section sites nationwide. It is special purpose photography for government land-use studies and not available for reproduction or resale . </note.n.86><note.n.87>Boynton Beach , Florida , March 15 , 2010 The Women of Note Chorus has announced the retirement of musical and artistic director , Holly St. John. Ms. St. John is the founding director of the Women of Note , an exceptional women ' s a cappella barbershop chorus that was founded in 1996. The Women of Note Chorus has been entertaining South Florida audiences with their beautiful a cappella four-part harmony , innovative choreography and dazzling costumes since its inception. Under Ms. St. John ' s direction , the chorus has earned several 1st place medals in their division in regional competition as well as the distinction of World ' s Best Small Chorus in 2005 . </note.n.87><note.n.88>The note had a safe conduct pass on the back showing a photograph of a French soldier accepting a rifle from a smiling guerrilla and his comrade who are being paid cash. The text on back is in French and Arabic , and says in French : </note.n.88><note.n.89>Level-2 statistics upload. The data buffer contains the data structure x25l2stat_type ( defined in zcomx25.h ) . Note that the values in this structure are not absolute values , but the number of occurrences of each type of condition since the last time the statistics were uploaded . </note.n.89><note.n.90>State Bank of New Brunswick , 18__. Curly haired girl at left , dogs head upper left center , 2 maids and horse with shield lower right , green ornamentation , ornate green back. Boldly printed on bright , white paper , this well engraved note has excellent eye appeal. the back design iswonderfully detailed and very well done. Lots to like about this note ! </note.n.90><note.n.91>Lendvai also makes note of the use of the Pitch Axis , which may be viewed as a method of assigning functional harmonic significance to pitch classes. One of the pitches must be noted as the 'tonic ' , following this , the interval from tonic determines the functional role that each note performs. The remaining functions include the dominant and the subdominant. Each of these acts as a category , containing four notes that are functionally equivalent within the musical context . </note.n.91><note.n.92>Lump Sum Payment with ( or without ) Interest : These types of loans are frequently used when loans come from friends or family. They are best used for short term loans with duration of twelve months or less. With a lump sum repayment plan the borrower repays the entire amount of the loan on a specified date. If interest is included , the amount of both principal and interest should be clearly outlined in the promissory note , along with the repayment date . </note.n.92><note.n.93>Originally , the value of money was determined by the intrinsic value of the material the money was made of , such as silver or gold. However , carrying around a lot of precious metal was cumbersome and often dangerous. As an alternative , banknotes would be issued. In financial terms , a note is a promise to pay someone money. Banknotes were originally a promise to pay the bearer an amount of precious metal stored in a vault somewhere. In this way the stored value ( usually in gold or silver coins ) backing the banknote could transfer ownership in exchange for goods or services . </note.n.93><note.n.94>In 1863 , in order to help finance the Civil War , a system of national banks was instituted by the National Currency Act. The banks each had the power to issue standardized national bank notes based on United States bonds held by the bank. The early national banking system had two main weaknesses : an inelastic currency; and a lack of liquidity. [ 2 ] During the last quarter of the 19th century and the beginning of the 20th century the United States economy went through a series of financial panics. [ 2 ] A particularly severe panic in 1907 provided the motivation for renewed demands for banking and currency reform. [ 3 ] The following year Congress enacted the Aldrich-Vreeland Act which provided for an emergency currency and established the National Monetary Commission to study banking and currency reform. [ 4 ] </note.n.94><note.n.95>All that we have mentioned has made it easy for us to provoke and bait this administration. All that we have to do is to send two mujahidin to the furthest point east to raise a piece of cloth on which is written al-Qaida , in order to make the generals race there to cause America to suffer human , economic , and political losses without their achieving for it anything of note other than some benefits for their private companies . </note.n.95><note.n.96>was the Act of July 17 , 1861 which allowed for $ 250 , 000 , 000 to be borrowed on the credit of the United States. Of this sum , up to $ 50 , 000 , 000 was authorized as non-interest bearing Treasury Notes , payable upon demand , in denominations less than fifty dollars and not less than ten dollars. These were called Demand Notes to distinguish them from the interest-bearing Treasury Notes in existence at the time. The promise to pay " on demand " was a new obligation for Treasury Notes ( though common on private banknotes ) but would spare the cash-strapped Treasury the intermediate step of selling an equivalent amount of debt by allowing it to use the notes as a currency to pay creditors directly. The notes were to be redeemable through the Assistant Treasurers ' offices at Philadelphia </note.n.96><note.n.97>Installing Jot is rather easy , in spite of the PC-centric manual ( " Double-click the Palm Desktop icon on your Windows desktop ... " ) . There are two files to install : Jot.prc and Jotdata.pdb. For the Macintosh , dragging the two files onto the SimpleInst icon and synchronizing installs Jot. Note that the application takes a full 115k on the Palm Pilot. For PalmPilot Personal users , sacrificing more than one-fifth of the memory for a handwriting recognition program may not be worth it. Even for PP Professional users , 115k is a significant chunk of memory . </note.n.97><note.n.98>116 elegxou kai anatrophj thj yeudwnumou gnwsewj ( cf. 1 Tim. vi. 20 ) . This work of Irenaeus , which is commonly known under its Latin title , Adversus Haereses ( Against Heresies ) , is still extant in a barbarous Latin version , of which we possess three mss. The original Greek is lost , though a great part of the first book can be recovered by means of extensive quotations made from it by Hippolytus and Epiphanius. The work is directed against the various Gnostic systems , among which that of Valentinus is chiefly attacked. The first book is devoted to a statement of their doctrines , the second to a refutation of them , and the remaining three to a presentation of the true doctrines of Christianity as opposed to the false positions of the Gnostics. The best edition of the original is that of Harvey : S. Irenaei libros quinque adv. Haereses. , Cambr. 1857 , 2 vols.; English translation in the Ante-Nicene Fathers , I. p. 309 ff. For the literature of the subject , see Schaff , II. p. 746 ff. On Irenaeus himself , see Book IV. chap. 21 , note 9 . </note.n.98><note.n.99>You will find out , the Federal Reserve owns your sneakers. How is this ? If you look on your Federal Reserve Note , you will find out it has daddy 's signature on it. It says , " Treasurer of the United States " on one side , and " Secretary of the Treasury " on the other side. These are 2 different distinct jurisdictions. The person whose signature is on the note , is the one who actually has the right to property of whatever you 've purchased. Is this not then the case with the boy , the grocer , and the father who signed the note which the boy gave to the grocer ? </note.n.99><note.n.100>Distinguished " explicit " responses , which were directly observable , from " implicit " responses , such as glandular secretions , that occurred inside the organism. Note that these implicit responses are not directly observable; they were potentially observable . </note.n.100></note.n.train>
\ No newline at end of file
diff --git a/examples/images/coning_off.png b/examples/images/coning_off.png
new file mode 100644
index 000000000..3fac0c68b
Binary files /dev/null and b/examples/images/coning_off.png differ
diff --git a/examples/images/local_singularity.png b/examples/images/local_singularity.png
new file mode 100644
index 000000000..819d1329c
Binary files /dev/null and b/examples/images/local_singularity.png differ
diff --git a/examples/images/locally_different.png b/examples/images/locally_different.png
new file mode 100644
index 000000000..6d2a35f1f
Binary files /dev/null and b/examples/images/locally_different.png differ
diff --git a/examples/local_hom_NLP_disambiguation.ipynb b/examples/local_hom_NLP_disambiguation.ipynb
new file mode 100644
index 000000000..f6dbc5e30
--- /dev/null
+++ b/examples/local_hom_NLP_disambiguation.ipynb
@@ -0,0 +1,378 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Local Homology NLP Use Cases: unsupervised word disambiguation\n",
+    "\n",
+    "In this tutorial we apply **local homology** to study natural language processing (NLP) data. We showcase how to use local homology to disambiguate words. In particular, we focus our analysis to disambiguate the word \"note\": either a musical frequency, a short text, the verb, or money.\n",
+    "\n",
+    "If you are looking at a static version of this notebook and would like to run its contents, head over to [GitHub](https://github.com/giotto-ai/giotto-tda/blob/master/examples/local_hom_NLP_disambiguation.ipynb) and download the source.\n",
+    "\n",
+    "## See also\n",
+    "  - [Topological feature extraction using VietorisRipsPersistence and PersistenceEntropy](https://giotto-ai.github.io/gtda-docs/latest/notebooks/vietoris_rips_quickstart.html) for a quick introduction to general topological feature extraction in ``giotto-tda``.\n",
+    "  - [Local Homology](https://giotto-ai.github.io/gtda-docs/latest/notebooks/local_homology.html), in which we introduce the ``RadiusLocalVietorisRips`` and ``KNeighborsLocalVietorisRips`` transformers used here are introduced.\n",
+    "\n",
+    "**License: AGPLv3**\n",
+    "\n",
+    "## The context\n",
+    "\n",
+    "Most modern machine learning techniques dealing with NLP data need to preprocess text data and trasform them into more standard objects: arrays. The process of transforming a word (or token, i.e. a word stripped out of its ending) into an array is called *word embedding*. \n",
+    "\n",
+    "There are of course more general techniques than simply transforming the single tokens: sentence embedding is one of the generalisations. However, for the purpose of this notebook, we will stick to word embeddings and in particular we will use a technique called [word2vec](https://en.wikipedia.org/wiki/Word2vec).\n",
+    "\n",
+    "The disadvantage of any word embedding techniques is that the same token is mapped to the same array. Hence, if two words have the same written form but different meanings (i.e., *homographic words*), such words will anyway be mapped to the same array!\n",
+    "\n",
+    "## The task \n",
+    "\n",
+    "Given the above introduction, our task is to *disambiguate words*, i.e. finding a way to differentiate homographic words by their meaning. Our approach consists of analysing the whole sentence in which the word appears and try to deduce the meaning of the word from its context (i.e. neighbouring words). The core idea of our proposal is based on the algebro-topological description of the space of word embeddings.\n",
+    "\n",
+    "## The main idea\n",
+    "\n",
+    "We are structuring our analysis on the assumptions that are clearly explained in [this paper](https://arxiv.org/pdf/2011.09413.pdf). In few words, the idea is that a word with multiple meaning sits on the sigular loci of the stratified space of the word embeddings. This sentence is in truth not formally correct, as there is yet no clear notion of what is the canonical topology in the word embedding space; nonetheless, the intuition behind these concepts can be explained pictorially:\n",
+    "\n",
+    "![sing](images/local_singularity.png)\n",
+    "\n",
+    "This picture represents the local shape of the word-embedding space: the context words are located over the four cones tipping at the word `mole` (the singularity). Hence, a sentence from a thriller containing the word `mole` would most probably be located on the north-west branch, and so on.\n",
+    "\n",
+    "## The goal of our exploration\n",
+    "\n",
+    "We would first like to stress that this notebook is merely exploratory and that there is no aim at making it a fully fledge ML pipeline for word disambiguation. \n",
+    "We would really like to understand if the shape of the cones around the singularity (i.e. the `mole` point) can be distinguished with **local homology**: if that is the case, then the geometric shape of a sentence lying on the word embedding stratified space correlates with the meaning of a word! This entails a new disambiguation technique."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Import PyData libraries\n",
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "%matplotlib inline\n",
+    "from sklearn.base import BaseEstimator, TransformerMixin\n",
+    "from sklearn.preprocessing import StandardScaler, FunctionTransformer\n",
+    "from sklearn.pipeline import make_pipeline, Pipeline\n",
+    "\n",
+    "# Import umap for dimensionality reduction and visualization\n",
+    "# umap-learn is not a requirement of giotto-tda, but is needed here.\n",
+    "from umap import UMAP\n",
+    "\n",
+    "# giotto-tda imports\n",
+    "from gtda.plotting import plot_point_cloud\n",
+    "from gtda.local_homology import KNeighborsLocalVietorisRips\n",
+    "from gtda.diagrams import PersistenceEntropy\n",
+    "\n",
+    "# Import needed NLP libraries\n",
+    "# gensim is not a requirement of giotto-tda, but is needed here.\n",
+    "from gensim.models import Word2Vec\n",
+    "from gensim.test.utils import common_texts\n",
+    "from gensim.parsing.preprocessing import remove_stopwords, stem"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## The dataset and its preprocessing\n",
+    "\n",
+    "The dataset contains many occurrences of the word \"note\" with the meanings stated above. The format is `.xml`, out of which we extract the plain text and do the standard preprocessing:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Preprocess the data\n",
+    "with open(\"data/note.n.xml\", \"r\") as f:\n",
+    "    content = f.read()\n",
+    "\n",
+    "# Split the sentences\n",
+    "temp_list = content.split(\"<note.n.\")\n",
+    "\n",
+    "# Remove stopwords\n",
+    "list_of_text = list(map(remove_stopwords, temp_list))\n",
+    "\n",
+    "# Make lower case and stem\n",
+    "list_of_text = list(map(stem, temp_list))\n",
+    "\n",
+    "refined_list = [list_of_text[i][1 + len(str(i)):-12 - len(str(i))]\n",
+    "                for i in range(2, len(list_of_text))]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Next, we extract senteces from our corpus and vectorize their words using ``Word2Vec``:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class PreprocessText(BaseEstimator, TransformerMixin):\n",
+    "    \"\"\"A basic class to transform a list of sentences (strings)\n",
+    "    into a list of arrays, each with 2 dimensions: (n_words, dim_emb_space)\n",
+    "\n",
+    "    Note that the output is a list of arrays, each of two dimensions: the\n",
+    "    first dimension is the number of words in a sentence, while the \n",
+    "    second dimension the word embedding dimension.\n",
+    "\n",
+    "    The ``item``parameter is useful to select, for the transform, which item\n",
+    "    to select.\n",
+    "    Set ``item = None`` to get the whole list\n",
+    "    \"\"\"\n",
+    "    def __init__(self, vector_size=30, window=5, min_count=1, workers=4):\n",
+    "        self.vector_size = vector_size\n",
+    "        self.window = window\n",
+    "        self.min_count = min_count\n",
+    "        self.workers = workers\n",
+    "\n",
+    "    def fit(self, X, y=None):\n",
+    "        all_words_in_sentences = list(map(str.split, X))\n",
+    "        self.word2vec = Word2Vec(sentences=all_words_in_sentences,\n",
+    "                                 vector_size=self.vector_size,\n",
+    "                                 window=self.window,\n",
+    "                                 min_count=self.min_count,\n",
+    "                                 workers=self.workers,\n",
+    "                                 seed=11)\n",
+    "        return self\n",
+    "\n",
+    "    def transform(self, X, y=None):\n",
+    "        all_words_in_sentences = list(map(str.split, X))\n",
+    "        list_of_vect_sentences = [self.word2vec.wv[all_words_in_sentences[i]]\n",
+    "                                  for i in range(len(all_words_in_sentences))]\n",
+    "        return list_of_vect_sentences\n",
+    "\n",
+    "pt = PreprocessText()\n",
+    "list_of_vect_sentences = pt.fit_transform(refined_list) # all vectorized sentences"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## The exploratory results\n",
+    "\n",
+    "Here below we see a couple of sentences containing the word \"note\" with the different meanings described above."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print(f\"\"\"Example of a preprocessed sentence where 'note' is used as a verb:\n",
+    "{refined_list[0] = }\n",
+    "\n",
+    "Example of preprocessed sentence where 'note' refers to music:\n",
+    "{refined_list[1] = }\"\"\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Local homology pipeline\n",
+    "\n",
+    "As explained in [Topological feature extraction using VietorisRipsPersistence and PersistenceEntropy](https://giotto-ai.github.io/gtda-docs/latest/notebooks/vietoris_rips_quickstart.html), \"persistence diagrams\" are a common and useful way to store information about the topology and geometry of data. Their content can be summarized and made interpretable even further by means of a variety of featurization methods.\n",
+    "\n",
+    "We will use a similar topological feature extraction method as in the [Local Homology](https://giotto-ai.github.io/gtda-docs/latest/notebooks/local_homology.html) notebook, and apply it to our Word2Vec embeddings!  In particular, we will fit our local homology transformer on either of the example sentences above (seen as point clouds of word vectors) and transform it on the fixed Word2Vec embedding for the word \"note\", to see if any topological differences arise."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Initialize a `KNeighborsLocalVietorisRips` local homology transformer.\n",
+    "n_neighbors = (5, 15)\n",
+    "homology_dimensions = (0, 1)\n",
+    "collapse_edges = True\n",
+    "n_jobs = -1\n",
+    "kn_lh = KNeighborsLocalVietorisRips(n_neighbors=n_neighbors,\n",
+    "                                    homology_dimensions=homology_dimensions,\n",
+    "                                    collapse_edges=collapse_edges,\n",
+    "                                    n_jobs=n_jobs)\n",
+    "\n",
+    "# Define a featurization method for persistence diagrams.\n",
+    "mod_pe =  make_pipeline(PersistenceEntropy(),\n",
+    "                        FunctionTransformer(func=lambda X: 2 ** X))\n",
+    "\n",
+    "#  Word2Vec embedding of \"note\"\n",
+    "note_w2v = pt.word2vec.wv[\"note\"].reshape(1, -1)\n",
+    "\n",
+    "# Fit the local homology transformer on the example sentence in which 'note' is a verb, and apply it to the \"note\" word vector\n",
+    "kn_lh.fit(list_of_vect_sentences[0])\n",
+    "mod_pe.fit_transform(kn_lh.transform(note_w2v))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Now fit the local homology transformer on the example sentence in which 'note' refers to music, and apply it to the \"note\" word vector again\n",
+    "kn_lh.fit(list_of_vect_sentences[1])\n",
+    "mod_pe.fit_transform(kn_lh.transform(note_w2v))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We now visualize the word2vec embeddings of two different sentences, using UMAP to perform dimensionality reduction:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def plotting_the_embedding(i, string):\n",
+    "    \"\"\"this function displays the word embedding space reduced\n",
+    "    to two dimensions by the UMAP algorithm. In yellow the word\n",
+    "    `note` is highlighted.\"\"\"\n",
+    "    value = None\n",
+    "    # for loop to find the instance of note\n",
+    "    for k in range(len(list_of_vect_sentences[i])):\n",
+    "        if (list_of_vect_sentences[i][k] == pt.transform((\"note\",))[0]).all():\n",
+    "            value = k\n",
+    "    temp = np.zeros((len(list_of_vect_sentences[i])))\n",
+    "    temp[value] = 1\n",
+    "\n",
+    "    reducer = UMAP()\n",
+    "\n",
+    "    scaled_point_cloud = StandardScaler().fit_transform(list_of_vect_sentences[i])\n",
+    "\n",
+    "    embedding = reducer.fit_transform(scaled_point_cloud)\n",
+    "\n",
+    "    plt.scatter(\n",
+    "        embedding[:, 0],\n",
+    "        embedding[:, 1], c = temp)\n",
+    "    plt.gca().set_aspect('equal', 'datalim')\n",
+    "    plt.title(f'Use of \"note\" as a {string}', fontsize=24)\n",
+    "\n",
+    "    # Example of sentence with the word \"note\"\n",
+    "    print(\"Preprocessed sentence: \")\n",
+    "    print(refined_list[i])\n",
+    "    kn_lh.fit(list_of_vect_sentences[i])\n",
+    "\n",
+    "    print(\"First and second Betti numbers:\")\n",
+    "    print(mod_pe.fit_transform(kn_lh.transform(np.array(pt.transform((\"note\",))[0], dtype=float))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Note as a verb\n",
+    "plotting_the_embedding(0, \"verb\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Musical note\n",
+    "plotting_the_embedding(1, \"musical note\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The technique seems promising: the results above show a clear distinction between the local shape of the neighborhoods for the word \"note\" with different meanings."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## A small scale statistical exploration"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We now look at 30 sentences where the word \"note\" takes different meanings which we have hand labelled.\n",
+    "For each 30 sentences, we look at the 0th and 1st dimension local homology around note and plot the obtained 2 dimensional pointcloud coloured by the meaning of \"note\" in the sentence."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "note_emb = pt.word2vec.wv[\"note\"]\n",
+    "note_loc_hom = []\n",
+    "for i in range(30):\n",
+    "    kn_lh = KNeighborsLocalVietorisRips(n_neighbors=(5, 15),\n",
+    "                                     homology_dimensions=(0, 1),\n",
+    "                                     collapse_edges=True,\n",
+    "                                     n_jobs=-1)\n",
+    "    sentence_emb = list_of_vect_sentences[i]\n",
+    "    kn_lh.fit(sentence_emb)\n",
+    "    note_loc_hom.append(mod_pe.fit_transform(kn_lh.transform(note_emb.reshape(1, -1)))[0])\n",
+    "\n",
+    "note_loc_hom = np.array(note_loc_hom)\n",
+    "\n",
+    "plt.scatter(\n",
+    "    note_loc_hom[:, 0],\n",
+    "    note_loc_hom[:, 1],\n",
+    "    c = [0, 1, 2, 2, 1, 1, 2, 2, 1, 3, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 3, 3, 3, 2, 1, 1, 3, 2, 2, 1])\n",
+    "plt.gca().set_aspect('equal', 'datalim')\n",
+    "plt.title('Local dimension around \"note\"', fontsize=24)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Conclusion\n",
+    "\n",
+    "The method seems promising! In particular, the meaning \"money\" (in yellow) seems to have very varying local dimensions, whereas the other classes seem to be more clustered together. However a lot more work is needed: especially, systematising the anylsis, finding the proper vectorisation of local homology, etc...\n",
+    "\n",
+    "We really hope that this notebook will tinkle your attention and suggest you new relevant research directions."
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.7"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/local_homology.ipynb b/examples/local_homology.ipynb
new file mode 100644
index 000000000..7d27cf92c
--- /dev/null
+++ b/examples/local_homology.ipynb
@@ -0,0 +1,356 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Local Homology\n",
+    "\n",
+    "In this tutorial we use tools from Topological Data Analysis (TDA) to study the local geometry of point clouds.\n",
+    "\n",
+    "If you are looking at a static version of this notebook and would like to run its contents, head over to [GitHub](https://github.com/giotto-ai/giotto-tda/blob/master/examples/local_homology.ipynb) and download the source.\n",
+    "\n",
+    "## See also\n",
+    "\n",
+    "- [Topological feature extraction using VietorisRipsPersistence and PersistenceEntropy](https://giotto-ai.github.io/gtda-docs/latest/notebooks/vietoris_rips_quickstart.html) for a quick introduction to general topological feature extraction in ``giotto-tda``.\n",
+    "- [Local Homology NLP Use Cases: Unsupervised text disambiguation](https://giotto-ai.github.io/gtda-docs/latest/notebooks/local_hom_NLP_disambiguation.html), in which the ideas presented here are applied to natural language data!\n",
+    "\n",
+    "**License: AGPLv3**"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "\n",
+    "from gtda.plotting import plot_point_cloud\n",
+    "from gtda.local_homology import KNeighborsLocalVietorisRips, RadiusLocalVietorisRips"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Dimensionality\n",
+    "\n",
+    "## What is \"dimensionality\", and how can we formalize it? \n",
+    "\n",
+    "In this tutorial we will learn how to algorithmically distinguish between possible *local geometries* around data points. Consider, for instance, the local geometry around the red dot in the three shapes below:\n",
+    "\n",
+    "![locally_different](images/locally_different.png)\n",
+    "\n",
+    "## How?\n",
+    "\n",
+    "We propose an answer to this question based on the notion of **local homology**. We estimate the \"local dimension\" around each point by studying the topological properties of the data points' neighbourhoods. We start by studying three shapes which differ by their dimensions, namely the line, the plane, and the cube."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "n = 10\n",
+    "line_1d = np.linspace(0, 1, num=n, endpoint=False)\n",
+    "\n",
+    "line = np.c_[np.zeros((n, 2)), line_1d]\n",
+    "plane = np.array([[0, y, z] for y in line_1d for z in line_1d])\n",
+    "cube = np.array([[x, y, z] for x in line_1d for y in line_1d for z in line_1d])\n",
+    "\n",
+    "line_plane_cube = np.vstack((line, plane + [0.5, -0.5, 0], cube + [1, -0.5, 0]))\n",
+    "plot_point_cloud(line_plane_cube)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## The method\n",
+    "\n",
+    "As explained in [Topological feature extraction using VietorisRipsPersistence and PersistenceEntropy](https://giotto-ai.github.io/gtda-docs/latest/notebooks/vietoris_rips_quickstart.html), \"persistence diagrams\" are a common and useful way to store information about the topology and geometry of data. Their content can be summarized and made interpretable even further by means of a variety of featurization methods.\n",
+    "\n",
+    "In this notebook, we show how to compute and featurize persistence diagrams for *local neighbourhoods* of all vectors in a dataset (e.g. in a point cloud), in such a way that each point's features capture the dataset's dimensionality around that point. We do this in steps:\n",
+    "1. create \"local point clouds\" around our data points and compute their persistence diagrams in a few chosen dimensions (more on this in \"How does it work?\" below);\n",
+    "2. featurize the \"local persistence diagrams\" using a modified version of the *persistence entropy* computed by the ``PersistenceEntropy`` transformer;\n",
+    "3. plot the original point cloud with its points coloured according to the value of the dimension-$i$ feature.\n",
+    "\n",
+    "Let us see this sequence of steps in action in the case of the point cloud representing a line. The colours will correspond to the value of the dimension-1 local topological feature. The local persistence diagrams are computed here using the ``KNeighborsLocalVietorisRips`` transformer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from gtda.diagrams import PersistenceEntropy\n",
+    "from sklearn.preprocessing import FunctionTransformer\n",
+    "from gtda.pipeline import make_pipeline\n",
+    "\n",
+    "## 1 - Initialize a local homology transformer. \n",
+    "# The notion of neighborhood here is determined by the number of closest neighbors to consider\n",
+    "n_neighbors = (1, 7)\n",
+    "homology_dimensions = (1, 2)\n",
+    "kn_lh = KNeighborsLocalVietorisRips(n_neighbors=n_neighbors,\n",
+    "                                    homology_dimensions=homology_dimensions)\n",
+    "\n",
+    "## 2 - Define a featurization method for persistence diagrams.\n",
+    "mod_pe =  make_pipeline(PersistenceEntropy(),\n",
+    "                        FunctionTransformer(func=lambda X: 2 ** X))\n",
+    "\n",
+    "# Summarize the steps above in a single end-to-end scikit-learn pipeline.\n",
+    "# Then, apply the pipeline\n",
+    "pipe = make_pipeline(kn_lh,\n",
+    "                     mod_pe)\n",
+    "loc_dim_features = pipe.fit_transform(line)\n",
+    "\n",
+    "## 3 - Plot the point cloud, colouring according to the value of the dimension-1 feature\n",
+    "# A simple helper function\n",
+    "def plot_coloured_cloud(point_cloud, colors):\n",
+    "    \"\"\"This is a custom plotting function, that plots the point cloud and colours \n",
+    "     the points according to a vector of colours.\"\"\"\n",
+    "    plotly_params = {\"trace\" : {\"marker\": {\"color\": colors,\n",
+    "                                           \"size\": 6,\n",
+    "                                           \"showscale\": True},\n",
+    "                                \"text\": [f\"color: {v}\" for v in colors]}}\n",
+    "    return plot_point_cloud(point_cloud, plotly_params=plotly_params)\n",
+    "\n",
+    "# Find the column index for homology dimension 1 in `loc_dim_features`\n",
+    "dimension = 1\n",
+    "dim_index = homology_dimensions.index(dimension)\n",
+    "colors = loc_dim_features[:, dim_index]\n",
+    "\n",
+    "# Plot the point cloud using the results in local homology dimension 1\n",
+    "plot_coloured_cloud(line, colors)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For each non-boundary point, the algorithm only detects one local feature, in dimension 1!\n",
+    "\n",
+    "Now let's try with the plane point cloud, colouring according to the value of the dimension-2 local topological feature.  The local persistence diagrams are computed here using the ``RadiusLocalVietorisRips`` transformer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Plane coloured with two-dimensional features.\n",
+    "# The notion of neighborhood here is determined by radii.\n",
+    "radii = (0.001, 0.3)\n",
+    "homology_dimensions = (1, 2, 3)\n",
+    "r_lh = RadiusLocalVietorisRips(radii=radii,\n",
+    "                               homology_dimensions=homology_dimensions)\n",
+    "\n",
+    "# Construct and apply the pipeline\n",
+    "pipe = make_pipeline(r_lh,\n",
+    "                     mod_pe)\n",
+    "loc_dim_features = pipe.fit_transform(plane)\n",
+    "\n",
+    "# Find the column index for homology dimension 1 in `loc_dim_features`\n",
+    "dimension = 2\n",
+    "dim_index = homology_dimensions.index(dimension)\n",
+    "colors = loc_dim_features[:, dim_index]\n",
+    "\n",
+    "# Plot the point cloud using the results in local homology dimension 1\n",
+    "plot_coloured_cloud(plane, colors)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "As for the line with its one-dimensional local features, the *two-dimensional* local features of the plane are equal to 1 everywhere except at the borders/boundaries."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Non-homogeneous example\n",
+    "\n",
+    "In the following example, we  consider a point cloud with varying dimensionality. We plot the three shapes next to each other. We encourage you to play around with the dimension parameter, to compare the local structure of points in the line, plane and cube!\n",
+    "\n",
+    "One sees that one-dimensional features are observed in all three shapes, but two-dimensional features only exist in the plane and three dimensional features only exist in the cube."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Here we set the `collapse_edges` parameter to True, which often improves performance in higher homology dimensions.\n",
+    "# We also set the `n_jobs` parameter to -1, to allow the algorithm to distribute across CPU cores!\n",
+    "radii = (0.01, 0.25)\n",
+    "homology_dimensions = (1, 2, 3)\n",
+    "collapse_edges = True\n",
+    "n_jobs = -1\n",
+    "r_lh = RadiusLocalVietorisRips(radii=radii,\n",
+    "                               homology_dimensions=homology_dimensions,\n",
+    "                               collapse_edges=collapse_edges,\n",
+    "                               n_jobs=n_jobs)\n",
+    "\n",
+    "# Construct and apply the pipeline\n",
+    "pipe = make_pipeline(r_lh,\n",
+    "                     mod_pe)\n",
+    "loc_dim_features = pipe.fit_transform(line_plane_cube)\n",
+    "\n",
+    "# Find the column index for homology dimension 1 in `loc_dim_features`.\n",
+    "# We encourage you to change the dimension parameter\n",
+    "dimension = 1\n",
+    "dim_index = homology_dimensions.index(dimension)\n",
+    "colors = loc_dim_features[:, dim_index]\n",
+    "\n",
+    "# Plot the point cloud using the results in the chosen local homology dimension\n",
+    "plot_coloured_cloud(line_plane_cube, colors)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## \"Non-manifold\" points|"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Our last example is inherently one-dimensional, but the point cloud still has some interesting local structures."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# First generate the point cloud\n",
+    "line1 = [[x, 0, 0] for x in np.arange(-1, 1, 1/10)]\n",
+    "line2 = [[0, y, 0] for y in np.arange(-1, 1, 1/10)]\n",
+    "line3 = [[x, 1, 0] for x in np.arange(-1, 1, 1/10)]\n",
+    "lines = np.array(line1 + line2 + line3)\n",
+    "\n",
+    "# Plot the point cloud\n",
+    "plot_point_cloud(lines)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Here, there are 4 different types of points:\n",
+    "- 'edge points', with no local structure;\n",
+    "- 'line points', which locally look like one-dimensional lines;\n",
+    "- 'intersection points' which locally look like an X;\n",
+    "- 'intersection points' which locally look like a T.\n",
+    "\n",
+    "The next block looks at the local homology features!"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Initialize the radius local homology transformer.\n",
+    "radii = (0.21, 0.31)\n",
+    "homology_dimensions = (0, 1)\n",
+    "n_jobs = -1\n",
+    "r_lh = RadiusLocalVietorisRips(radii=radii,\n",
+    "                               homology_dimensions=homology_dimensions,\n",
+    "                               n_jobs=n_jobs)\n",
+    "\n",
+    "# Construct and apply the pipeline\n",
+    "pipe = make_pipeline(r_lh,\n",
+    "                     mod_pe)\n",
+    "loc_dim_features = pipe.fit_transform(lines)\n",
+    "\n",
+    "# Find the column index for homology dimension 1 in `loc_dim_features`.\n",
+    "dimension = 1\n",
+    "dim_index = homology_dimensions.index(dimension)\n",
+    "colors = loc_dim_features[:, dim_index]\n",
+    "\n",
+    "# Plot the point cloud using the results in the chosen local homology dimension\n",
+    "plot_coloured_cloud(lines, colors)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Notice that the one-dimensional feature counts the branches at a given point!\n",
+    "\n",
+    "We encourage you to change the values of the ``radii`` parameter. If you are curious of what these parameters are, please scroll down to the 'How does it work?' section."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# How does it work?\n",
+    "\n",
+    "As the mathematics behind this algorithm is quite involved, we give an explanation for one-dimensional examples. We also give a picture for the process in a 2-dimensional example.\n",
+    "\n",
+    "A central theme in topology is the study of properties that are invariant under deformation. A popular one-dimensional invariant is the number of 'loops' in the space. For a graph, the number of 'loops' is the number of cycles of that graph. In the case of connected graphs this is equal to $1 - \\chi(G)$, where $\\chi(G) = |V| - |E|$ is the *Euler characteristic* of the graph, and $|V|$ and $|E|$ are the number of vertices and edges respectively. \n",
+    "\n",
+    "Let us consider *trees*, which are a particular kind of graph. Although all trees have Euler characteristic equal to $1$, they can have very different local structure. By computing this local structure, we can discriminate between topologically distinct trees (or graphs, or higher-dimensional objects). In order to compute the local structure around each data point with standard algorithms for point clouds or dissimilarity matrices, such as the highly-optimised routines used by ``VietorisRipsPersistence``, we use a few tricks!\n",
+    "\n",
+    "The first step is to discard points that are far away from the point under consideration, since they do not affect the local geometry around it. We thus only keep points that are \"not far\" from our point, according to a certain criterion. For the ``RadiusLocalVietorisRips`` transformer, the criterion is that the distance from the chosen point should be less than or equal to the second entry in the ``radii`` pair. For the ``KNeighborsVietorisRips`` transformer, we only include the $k$-nearest neighbors to our chosen point, where $k$ is the second entry in the ``n_neighbors`` pair.\n",
+    "\n",
+    "The second step is the one making loops reflect dimensionality. From a line, how can we obtain a loop? We stick the two ends of the line together! This trick can be thought of as \"coning off\" the endpoints: adding an extra point and linking some points (the furthest points) to this extra point. In the case of the ``RadiusLocalVietorisRips`` transformer, our algorithm cones off the points that lie in between the first and second entries in ``radii``, as depicted below. For the ``KNeighborsVietorisRips`` transformer, we cone off in an analogous way according to the ``n_neighbors`` pair.\n",
+    "\n",
+    "So what is the Euler characteristic of the result of applying this procedure to an \"X shape\" graph, which could be the local neighborhood around a point in a larger graph? The result after \"coning off\" has two vertices (at the intersection points) and 4 edges, so the Euler characteristic is $-2$! This reflects the complexity at the point that we are considering. (Notice also that a loop has Euler characteristic $0$, and a triple intersection will give Euler characteristic $-1$.)\n",
+    "\n",
+    "As a 2-dimensional example of this procedure, consider a disk (again, you can think of this as the local neighbourhood around a chosen point). By coning off the outer part of this disk, we end up with a sphere. And a sphere's only non-trivial topological features are 2-dimensional."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![coning_off](images/coning_off.png)\n",
+    "<div style=\"text-align: left\">\n",
+    "   <p style=\"text-align: center;\">  Coning off a cross and a disk.</p>\n",
+    "</div>"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/lorenz_attractor.ipynb b/examples/lorenz_attractor.ipynb
index 4e39d1e65..70cf22be1 100644
--- a/examples/lorenz_attractor.ipynb
+++ b/examples/lorenz_attractor.ipynb
@@ -632,18 +632,11 @@
    "source": [
     "pipeline[-1].plot(X_diagrams, sample=window_number)"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
    "name": "python3"
   },
@@ -657,7 +650,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.5"
+   "version": "3.9.2"
   }
  },
  "nbformat": 4,
diff --git a/gtda/__init__.py b/gtda/__init__.py
index d79bbd4ee..d05e79d1f 100644
--- a/gtda/__init__.py
+++ b/gtda/__init__.py
@@ -13,5 +13,6 @@
     'externals',
     'utils',
     'metaestimators',
+    'local_homology',
     '__version__'
     ]
diff --git a/gtda/local_homology/__init__.py b/gtda/local_homology/__init__.py
new file mode 100644
index 000000000..7d34f9aa3
--- /dev/null
+++ b/gtda/local_homology/__init__.py
@@ -0,0 +1,11 @@
+"""The module :mod:`gtda.local_homology` implements transformers
+to generate local persistence diagrams."""
+
+
+from .simplicial import KNeighborsLocalVietorisRips, \
+    RadiusLocalVietorisRips
+
+__all__ = [
+    'KNeighborsLocalVietorisRips',
+    'RadiusLocalVietorisRips',
+    ]
diff --git a/gtda/local_homology/simplicial.py b/gtda/local_homology/simplicial.py
new file mode 100644
index 000000000..5ff46359d
--- /dev/null
+++ b/gtda/local_homology/simplicial.py
@@ -0,0 +1,455 @@
+from numbers import Real
+from types import FunctionType
+
+import numpy as np
+import warnings
+
+from scipy.spatial.distance import pdist, squareform
+from sklearn.neighbors import KNeighborsTransformer, RadiusNeighborsTransformer
+from sklearn.base import BaseEstimator, TransformerMixin
+from sklearn.utils import check_array
+from sklearn.utils.validation import check_is_fitted
+
+from gtda.homology import VietorisRipsPersistence
+from gtda.utils.intervals import Interval
+from gtda.utils.validation import validate_params
+from gtda.plotting import plot_diagram
+from gtda.base import PlotterMixin
+from gtda.utils._docs import adapt_fit_transform_docs
+
+
+class LocalVietorisRipsBase(BaseEstimator,
+                            TransformerMixin,
+                            PlotterMixin):
+    """Base class for KNeighboursLocalVietorisRips and RadiusLocalVietorisRips.
+    LocalVietorisRipsBase is not meant to be used. Please see documentation
+    for KNeighboursLocalVietorisRips and RadiusLocalVietorisRips.
+
+    """
+
+    def __init__(self, metric="euclidean", homology_dimensions=(1, 2),
+                 neighborhood_params=(1, 2), collapse_edges=False,
+                 n_jobs=None):
+        """Initializes the base class by setting the basic parameters.
+        For more specific description, see specific children classes."""
+        # metric for the point cloud
+        self.metric = metric
+
+        # topological dimension of features to be computed
+        self.homology_dimensions = homology_dimensions
+
+        # Tuple of parameters defining "neighborhoods" of points. These
+        # parameters are input in the Transformer objects determining what
+        # points lie in the "neighborhoods" of each points. The points outside
+        # the "neighborhood" defined by the largest entry are discarded, and
+        # the points between the smaller and largest "neighborhoods" are "coned
+        # off". See more in the corresponding fit methods.
+        self.neighborhood_params = neighborhood_params
+
+        # parameter to feed into the homology transformer
+        self.collapse_edges = collapse_edges
+
+        self.n_jobs = n_jobs
+
+    def fit(self, X, y=None):
+        """Initializes the object used for computing persistence homology,
+        checks that the parameters were initialized correctly.
+
+        """
+        # object used to compute persistence diagrams
+        self.homology = VietorisRipsPersistence(
+            metric="precomputed",
+            collapse_edges=self.collapse_edges,
+            homology_dimensions=self.homology_dimensions,
+            n_jobs=self.n_jobs
+            )
+        # make sure the neighborhood_params has been set correctly.
+        if self.neighborhood_params[0] > self.neighborhood_params[1]:
+            warnings.warn("First `neighborhood_params` is larger than second. "
+                          "The values are permuted. ")
+            self.neighborhood_params = (self.neighborhood_params[1],
+                                        self.neighborhood_params[0])
+        if self.neighborhood_params[1] == 0:
+            warnings.warn("Second `neighborhood_params` is less than 0. "
+                          "Second radius set to 1. ")
+            self.radii = (self.radii[0], 1)
+        if self.neighborhood_params[0] == self.neighborhood_params[1]:
+            warnings.warn("For meaningful features, first "
+                          "`neighborhood_params` should be strictly smaller "
+                          "than second.")
+        return self
+
+    def transform(self, X, y=None):
+        """Computes the local persistence diagrams at each element of X, and
+        returns a list of persistence diagrams, indexed as the points of X.
+        This is done in several steps:
+            - First compute the nearest neighbors in the point cloud that
+            was fitted on, for both values in n_neighbors.
+            - For each point, compute the relevant points (corresponding to
+            the larger neighborhood_params value), the close points
+            (corresponding to the smaller neighborhood_params value), and the
+            annulus to cone off (relevant points, but not close points).
+            Compute the distance matrix of the relevant points, and add an
+            additional row and column corresponding to the coning off point.
+            - Finally compute the persistence diagrams of each coned matrices.
+
+        Parameters
+        ----------
+        X : ndarray of shape (n_points, dimension)
+             Input data representing  point cloud:
+             an array of shape ``(n_points, n_dimensions)``.
+
+        y : None
+            There is no need for a target in a transformer, yet the pipeline
+            API requires this parameter.
+
+        Returns
+        -------
+        Xt : ndarray of shape (n_samples, n_features, 3)
+            Array of persistence diagrams computed from the feature arrays.
+            ``n_features`` equals :math:`\\sum_q n_q`, where :math:`n_q`
+            is the maximum number of topological features in dimension
+            :math:`q` across all samples in `X`.
+
+        """
+        check_is_fitted(self)
+        Xt = check_array(X, accept_sparse=False)
+
+        # sparse binary matrices where rows indicate the indices of points
+        # which are nearest neighbors to the row's index point.
+        Xt_close = self.close_neighbors_.transform(Xt)
+        Xt_relevant = self.relevant_neighbors_.transform(Xt)
+
+        coned_mats = []
+        for i in range(len(Xt)):
+            # get indices of points close to point at index i
+            close_indices = Xt_close.getrow(i).indices
+            # get indices of points in second "neighborhood"
+            relevant_indices = Xt_relevant.getrow(i).indices
+            annulus_indices = list(set(relevant_indices) - set(close_indices))
+            # Order them such that the last ones are the ones to cone off
+            reordered_relevant_indices = np.concatenate((close_indices,
+                                                         annulus_indices))
+            if len(close_indices) == 0:
+                # The coned off space retracts to the cone point
+                coned_mat = np.zeros((1, 1))
+            else:
+                # Fetch the coordinates
+                relevant_points = [self.relevant_neighbors_._fit_X[int(y)]
+                                   for y in reordered_relevant_indices]
+                # Dense distance matrix between all relevant points
+                local_mat = squareform(pdist(relevant_points,
+                                             metric=self.metric))
+                # Now add the cone point:
+                new_row = np.concatenate((np.ones(len(close_indices))*np.inf,
+                                          np.zeros(len(annulus_indices))))
+                new_col = np.concatenate((new_row, [0]))
+                pre_cone = np.concatenate((local_mat, [new_row]))
+                coned_mat = np.concatenate(
+                                           (pre_cone, np.array([new_col],
+                                                               dtype=float).T),
+                                           axis=1)
+            coned_mats += [coned_mat]
+        # Compute the Vietoris Rips Persistence diagrams
+        Xt = self.homology.fit_transform(coned_mats)
+        return Xt
+
+    @staticmethod
+    def plot(Xt, sample=0, homology_dimensions=None, plotly_params=None):
+        """Plot a sample from a collection of persistence diagrams, with
+        homology in multiple dimensions.
+
+        Parameters
+        ----------
+        Xt : ndarray of shape (n_samples, n_features, 3)
+            Collection of persistence diagrams, such as returned by
+            :meth:`transform`.
+
+        sample : int, optional, default: ``0``
+            Index of the sample in `Xt` to be plotted.
+
+        homology_dimensions : list, tuple or None, optional, default: ``None``
+            Which homology dimensions to include in the plot. ``None`` means
+            plotting all dimensions present in ``Xt[sample]``.
+
+        plotly_params : dict or None, optional, default: ``None``
+            Custom parameters to configure the plotly figure. Allowed keys are
+            ``"traces"`` and ``"layout"``, and the corresponding values should
+            be dictionaries containing keyword arguments as would be fed to the
+            :meth:`update_traces` and :meth:`update_layout` methods of
+            :class:`plotly.graph_objects.Figure`.
+
+        Returns
+        -------
+        fig : :class:`plotly.graph_objects.Figure` object
+            Plotly figure.
+
+        """
+        return plot_diagram(
+            Xt[sample], homology_dimensions=homology_dimensions,
+            plotly_params=plotly_params
+            )
+
+
+@adapt_fit_transform_docs
+class KNeighborsLocalVietorisRips(LocalVietorisRipsBase):
+    """Given a :ref:`point cloud <finite_metric_spaces_and_point_clouds>` in
+    Euclidean space, or an abstract :ref:`metric space
+    <finite_metric_spaces_and_point_clouds>` encoded by a distance matrix,
+    information about the local topology around each point is summarized in a
+    collection of persistence diagrams.
+
+    This is done by first isolating appropriate neighborhoods around each point
+    using a nearest neighbor transformer, then "coning off" points in an
+    annulus around each point, and finally computing the corresponding
+    associated persistence diagram. The output can then be used to explore the
+    point cloud, or fed into a vectorizer to obtain features.
+
+    Parameters
+    ----------
+    metric : string or callable, optional, default: ``"euclidean"``
+        Input data is to be interpreted as a point cloud (i.e. feature arrays),
+        and `metric`determines a rule with which to calculate distances between
+        pairs of points (i.e. row vectors). If `metric` is a string, it must be
+        one of the options allowed by :func:`scipy.spatial.distance.pdist`
+        for its metric parameter, or a metric listed in
+        :obj:`sklearn.pairwise.PAIRWISE_DISTANCE_FUNCTIONS`, including
+        ``"euclidean"``, ``"manhattan"`` or ``"cosine"``. If `metric` is a
+        callable, it should take pairs of vectors (1D arrays) as input and, for
+        each two vectors in a pair, it should return a scalar indicating the
+        distance/dissimilarity between them.
+
+    n_neighbors: tuple, optional, default: ``(10, 50)``, has to
+        consist of two non-negative integers. This defines the number of points
+        in the first and second neighborhoods considered.
+
+    homology_dimensions: tuple, optional, default: ``(1, 2)``. Dimensions
+        (non-negative integers) of the topological features to be detected.
+
+    collapse_edges : bool, optional, default: ``False``
+        Whether to run the edge collapse algorithm in [1]_ prior to the
+        persistent homology computation (see the Notes). Can reduce the runtime
+        dramatically when the data or the maximum homology dimensions are
+        large.
+
+    n_jobs : int or None, optional, default: ``None``
+        The number of jobs to use for the computation. ``None`` means 1 unless
+        in a :obj:`joblib.parallel_backend` context. ``-1`` means using all
+        processors.
+
+    References
+    ----------
+
+    .. [1] J.-D. Boissonnat and S. Pritam, "Edge Collapse and Persistence of
+           Flag Complexes"; in *36th International Symposium on Computational
+           Geometry (SoCG 2020)*, pp. 19:1–19:15,
+           Schloss Dagstuhl-Leibniz–Zentrum für Informatik, 2020;
+           `DOI: 10.4230/LIPIcs.SoCG.2020.19
+           <https://doi.org/10.4230/LIPIcs.SoCG.2020.19>`_.
+
+    """
+
+    _hyperparameters = {
+        "metric": {"type": (str, FunctionType)},
+        "n_neighbors": {"type": (tuple, list),
+                        "of": {type: int,
+                               "in": Interval(1, np.inf, closed="left")}
+                        },
+        "homology_dimensions": {
+            "type": (tuple, list),
+            "of": {"type": int, "in": Interval(0, np.inf, closed="left")}
+            },
+        "collapse_edges": {"type": bool}
+        }
+
+    def __init__(self, metric="euclidean", homology_dimensions=(1, 2),
+                 n_neighbors=(1, 2), collapse_edges=False, n_jobs=None):
+        self.n_neighbors = n_neighbors
+        super().__init__(metric=metric,
+                         homology_dimensions=homology_dimensions,
+                         neighborhood_params=self.n_neighbors,
+                         collapse_edges=collapse_edges,
+                         n_jobs=n_jobs)
+
+    def fit(self, X, y=None):
+        """Initiates and fits the transformers that efficiently computes the
+        nearest neighbors of given points.
+        This method is here to implement the usual scikit-learn API and hence
+        work in pipelines.
+
+        Parameters
+        ----------
+        X : ndarray of shape (n_points, dimension)
+            Input data representing  point cloud. Can be either
+            a point cloud: an array of shape ``(n_points, n_dimensions)``.
+
+        y : None
+            There is no need for a target in a transformer, yet the pipeline
+            API requires this parameter.
+
+        Returns
+        -------
+        self : object
+
+        """
+        super().fit(X)
+
+        validate_params(
+            self.get_params(), self._hyperparameters, exclude=["n_jobs"])
+        check_array(X, accept_sparse=False)
+
+        # make sure that the parameters are set correctly
+        self.size_ = len(X)
+        if self.size_ <= self.neighborhood_params[0]:
+            warnings.warn("First n_neighbors is too large to be relevant. "
+                          "Consider reducing it.")
+            self.neighborhood_params = (self.size_-1, self.size_)
+        if self.size_ < self.neighborhood_params[1]:
+            warnings.warn("Second `n_neighbors` is too large to be "
+                          "relevant. Consider reducing it. ")
+            self.neighborhood_params = (self.neighborhood_params[0],
+                                        self.size_)
+
+        # Objects used for finding nearest neighbors
+        self.close_neighbors_ = KNeighborsTransformer(
+            mode="connectivity",
+            n_neighbors=self.neighborhood_params[0],
+            metric=self.metric,
+            n_jobs=self.n_jobs
+            )
+
+        self.relevant_neighbors_ = KNeighborsTransformer(
+            mode="connectivity",
+            n_neighbors=self.neighborhood_params[1],
+            metric=self.metric,
+            n_jobs=self.n_jobs
+            )
+
+        self.close_neighbors_.fit(X)
+        self.relevant_neighbors_.fit(X)
+        return self
+
+
+@adapt_fit_transform_docs
+class RadiusLocalVietorisRips(LocalVietorisRipsBase):
+    """Given a :ref:`point cloud <finite_metric_spaces_and_point_clouds>` in
+    Euclidean space, or an abstract :ref:`metric space
+    <finite_metric_spaces_and_point_clouds>` encoded by a distance matrix,
+    information about the local topology around each point is summarized in a
+    collection of persistence diagrams.
+
+    This is done by first isolating appropriate neighborhoods around each point
+    using a radius neighbor transformer, then "coning off" points in an annulus
+    around each point, and finally computing the corresponding associated
+    persistence diagram. The output can then be used to explore the point
+    cloud, or fed into a vectorizer to obtain features.
+
+    Parameters
+    ----------
+    metric : string or callable, optional, default: ``"euclidean"``
+        Input data is to be interpreted as a point cloud (i.e. feature arrays),
+        and `metric`determines a rule with which to calculate distances between
+        pairs of points (i.e. row vectors). If `metric` is a string, it must be
+        one of the options allowed by :func:`scipy.spatial.distance.pdist` for
+        its `metric` parameter, or a metric listed in
+        :obj:`sklearn.pairwise.PAIRWISE_DISTANCE_FUNCTIONS`, including
+        ``"euclidean"``, ``"manhattan"`` or ``"cosine"``. If `metric` is a
+        callable, it should take pairs of vectors (1D arrays) as input and, for
+        each two vectors in a pair, it should return a scalar indicating the
+        distance/dissimilarity between them.
+
+    radii: tuple, optional, default: ``(0.0, 1.0)`` has to consist of two
+    non-negative floats. This determines the radius of the first and second
+    neighborhood around points considered.
+
+    homology_dimensions: tuple, optional, default: ``(1, 2)``. Dimensions
+        (non-negative integers) of the topological features to be detected.
+
+    collapse_edges : bool, optional, default: ``False``
+        Whether to run the edge collapse algorithm in [1]_ prior to the
+        persistent homology computation (see the Notes). Can reduce the runtime
+        dramatically when the data or the maximum homology dimensions are
+        large.
+
+    n_jobs : int or None, optional, default: ``None``
+        The number of jobs to use for the computation. ``None`` means 1 unless
+        in a :obj:`joblib.parallel_backend` context. ``-1`` means using all
+        processors.
+
+    References
+    ----------
+    .. [1] J.-D. Boissonnat and S. Pritam, "Edge Collapse and Persistence of
+           Flag Complexes"; in *36th International Symposium on Computational
+           Geometry (SoCG 2020)*, pp. 19:1–19:15,
+           Schloss Dagstuhl-Leibniz–Zentrum für Informatik, 2020;
+           `DOI: 10.4230/LIPIcs.SoCG.2020.19
+           <https://doi.org/10.4230/LIPIcs.SoCG.2020.19>`_.
+
+    """
+
+    _hyperparameters = {
+        "metric": {"type": (str, FunctionType)},
+        "radii": {"type": (tuple, list),
+                  "of": {type: Real, "in": Interval(0, np.inf, closed="left")}
+                  },
+        "homology_dimensions": {
+            "type": (tuple, list),
+            "of": {"type": int, "in": Interval(0, np.inf, closed="left")}
+            },
+        "collapse_edges": {"type": bool}
+        }
+
+    def __init__(self, metric="euclidean", homology_dimensions=(1, 2),
+                 radii=(1, 2), collapse_edges=False, n_jobs=None):
+        self.radii = radii
+        super().__init__(metric=metric,
+                         homology_dimensions=homology_dimensions,
+                         neighborhood_params=self.radii,
+                         collapse_edges=collapse_edges,
+                         n_jobs=n_jobs)
+
+    def fit(self, X, y=None):
+        """Initiates and fits the transformers that efficiently computes the
+        nearest neighbors of given points.
+        This method is here to implement the usual scikit-learn API and hence
+        work in pipelines.
+
+        Parameters
+        ----------
+        X : ndarray of shape (n_points, dimension)
+            Input data representing  point cloud. Can be either
+            a point cloud: an array of shape ``(n_points, n_dimensions)``.
+
+        y : None
+            There is no need for a target in a transformer, yet the pipeline
+            API requires this parameter.
+
+        Returns
+        -------
+        self : object
+
+        """
+        super().fit(X)
+
+        validate_params(
+            self.get_params(), self._hyperparameters, exclude=["n_jobs"])
+        check_array(X, accept_sparse=False)
+
+        # Objects used for finding nearest neighbors
+        self.close_neighbors_ = RadiusNeighborsTransformer(
+            mode="connectivity",
+            radius=self.neighborhood_params[0],
+            metric=self.metric,
+            n_jobs=self.n_jobs
+            )
+
+        self.relevant_neighbors_ = RadiusNeighborsTransformer(
+            mode="connectivity",
+            radius=self.neighborhood_params[1],
+            metric=self.metric,
+            n_jobs=self.n_jobs
+            )
+
+        self.close_neighbors_.fit(X)
+        self.relevant_neighbors_.fit(X)
+        return self
diff --git a/setup.py b/setup.py
index 700e83732..9d03c99e7 100755
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,9 @@
                   "examples": ["jupyter",
                                "pandas",
                                "openml",
-                               "matplotlib"]}
+                               "matplotlib",
+                               "gensim",
+                               "umap-learn"]}
 
 
 def combine_requirements(base_keys):


Test patch:
diff --git a/gtda/local_homology/tests/__init__.py b/gtda/local_homology/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gtda/local_homology/tests/test_simplicial.py b/gtda/local_homology/tests/test_simplicial.py
new file mode 100644
index 000000000..e1472edc1
--- /dev/null
+++ b/gtda/local_homology/tests/test_simplicial.py
@@ -0,0 +1,109 @@
+from hypothesis import given, settings
+from hypothesis.strategies import floats, integers, composite, lists
+
+from gtda.local_homology.simplicial import KNeighborsLocalVietorisRips, \
+      RadiusLocalVietorisRips
+
+
+@composite
+def gen_3d_point_cloud(draw):
+    """ Generates point clouds as lists of floats in the unit cube of
+        size between 2 and 20."""
+    three_floats = lists(floats(min_value=0, max_value=1), min_size=3,
+                         max_size=3, unique=True)
+    return draw(lists(three_floats, min_size=2, max_size=20))
+
+
+@composite
+def gen_dimensions(draw):
+    """ Generates dimension as tuples of integers."""
+    return tuple(draw(lists(integers(min_value=1, max_value=10),
+                      min_size=1, max_size=5, unique=True)))
+
+
+@composite
+def gen_epsilon(draw):
+    """ Generates radii as floats. """
+    epsilon1 = draw(floats(min_value=0, max_value=1))
+    # max to in min_value to avoid warning
+    epsilon2 = draw(floats(min_value=max(epsilon1 + 1e-3, 1e-3),
+                           max_value=1 + 2e-3))
+    return (epsilon1, epsilon2)
+
+
+@composite
+def gen_n_neighbors(draw):
+    """Generates number of neighbors as integers. """
+    n_neighbor1 = draw(integers(min_value=1, max_value=20))
+    n_neighbor2 = draw(integers(min_value=n_neighbor1+1, max_value=30))
+    return (n_neighbor1, n_neighbor2)
+
+
+@settings(deadline=None)
+@given(point_cloud=gen_3d_point_cloud(),
+       point_cloud2=gen_3d_point_cloud(),
+       dims=gen_dimensions(),
+       n_neighbors=gen_n_neighbors())
+def test_KNeighborsLocalVietoris(point_cloud, point_cloud2, dims,
+                                 n_neighbors):
+    # fit transform on same point cloud:
+    X = point_cloud
+
+    # 'min' below to avoid warnings
+    n_neighbors = (min(len(X)-1, n_neighbors[0]),
+                   min(len(X), n_neighbors[1]))
+
+    lh_kn = KNeighborsLocalVietorisRips(metric='euclidean',
+                                        n_neighbors=n_neighbors,
+                                        homology_dimensions=dims,
+                                        n_jobs=-1)
+    lh_kn.fit(X)
+    lh_kn.transform(X)
+    lh_kn = KNeighborsLocalVietorisRips(metric='euclidean',
+                                        n_neighbors=n_neighbors,
+                                        homology_dimensions=dims,
+                                        collapse_edges=True,
+                                        n_jobs=-1)
+    lh_kn.fit_transform(X)
+
+    # fit and transform on different point clouds:
+    Y = point_cloud2
+    lh_kn = KNeighborsLocalVietorisRips(metric='euclidean',
+                                        n_neighbors=n_neighbors,
+                                        homology_dimensions=dims,
+                                        n_jobs=-1)
+    lh_kn.fit(X)
+    lh_kn.transform(Y)
+
+
+@settings(deadline=None)
+@given(point_cloud=gen_3d_point_cloud(),
+       point_cloud2=gen_3d_point_cloud(),
+       dims=gen_dimensions(),
+       radii=gen_epsilon())
+def test_RadiusLocalVietoris(point_cloud, point_cloud2, dims, radii):
+    # fit transform on same point cloud:
+    X = point_cloud
+
+    lh_rad = RadiusLocalVietorisRips(metric='euclidean',
+                                     radii=radii,
+                                     homology_dimensions=dims,
+                                     collapse_edges=True,
+                                     n_jobs=-1)
+    lh_rad.fit(X)
+    lh_rad.transform(X)
+    lh_rad = RadiusLocalVietorisRips(metric='euclidean',
+                                     radii=radii,
+                                     homology_dimensions=dims,
+                                     n_jobs=-1)
+    lh_rad.fit_transform(X)
+
+    # fit and transform on different point clouds:
+    Y = point_cloud2
+    lh_rad = RadiusLocalVietorisRips(metric='euclidean',
+                                     radii=radii,
+                                     homology_dimensions=dims,
+                                     collapse_edges=True,
+                                     n_jobs=-1)
+    lh_rad.fit(X)
+    lh_rad.transform(Y)