@@ -96,40 +96,86 @@ typedef knncolle_annoy::AnnoyBuilder<
9696
9797To save and reload Annoy indices from disk, we need to register a loading function into ** knncolle** 's ` load_prebuilt() ` registry.
9898This is a little complicated as we must decide on which combinations of types and distances we want to deal with.
99- Here , we only consider the obvious distance metrics and the defaults for the internal Annoy types,
99+ In the example below , we only consider the obvious distance metrics and the defaults for the internal Annoy types,
100100though more combinations could also be supported at the cost of larger binaries and longer compile times.
101101
102102``` cpp
103103auto & reg = knncolle::load_prebuilt_registry<int , double , double >();
104104reg[knncolle_annoy::save_name] = [](const std::string& prefix) -> Prebuilt<int , double , double >* {
105- auto config = knncolle_annoy::scan_prebuilt_save_config (prefix);
105+ auto config = knncolle_annoy::load_annoy_prebuilt_types (prefix);
106106
107- // Maybe add some checks that the types are as expected .
108- if (config.index != knncolle_annoy ::get_numeric_type<int>()) {
107+ // Checks that the AnnoyIndex_ and AnnoyData_ types are the same as the defaults .
108+ if (config.index != knncolle ::get_numeric_type<int>()) {
109109 throw std::runtime_error("unexpected type for the Annoy index");
110110 }
111- if (config.data != knncolle_annoy ::get_numeric_type<float>()) {
111+ if (config.data != knncolle ::get_numeric_type<float>()) {
112112 throw std::runtime_error("unexpected type for the Annoy data");
113113 }
114114
115115 if (config.distance == "euclidean") {
116- return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Euclidean>();
116+ return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Euclidean>(prefix );
117117 } else if (config.distance != "manhattan") {
118118 throw std::runtime_error("unknown Annoy distance");
119119 }
120- return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Manhattan>();
120+ return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Manhattan>(prefix );
121121};
122122```
123123
124124Then we can save and reload the ` Prebuilt ` Annoy indices.
125- Note the caveats on ` knncolle::Prebuilt::save() ` - specifically, the files are not guaranteed to be portable between machines or even different versions of ** knncolle_annoy** .
125+ Note the caveats on ` knncolle::Prebuilt::save() ` -
126+ specifically, the files are not guaranteed to be portable between machines or even different versions of ** knncolle_annoy** .
126127
127128``` cpp
128- std::string path_prefix = " anno /location/here_" ;
129+ std::string path_prefix = " annoy /location/here_" ;
129130an_index.save(path_prefix);
130131auto reloaded = knncolle::load_prebuilt(path_prefix);
131132```
132133
134+ For custom Annoy-related types and distances, users can define their own saving and loading methods.
135+ For example, if we had a custom distance:
136+
137+ ``` cpp
138+ // Mock up a custom distance.
139+ class MyCustomDistance : public Annoy ::Euclidean {};
140+
141+ // Define a function to save information about this custom distance during .save() calls.
142+ knncolle_annoy::custom_save_for_annoy_data<MyCustomDistance >() = [ ] (const std::string& prefix) -> void {
143+ const auto path = prefix + ".custom_distance";
144+ const std::string msg = "hi this is a custom distance";
145+ knncolle::quick_save(path, msg.c_str(), msg.size());
146+ };
147+
148+ // Make a new loading function that uses the saved custom information.
149+ reg[ knncolle_annoy::save_name] = [ ] (const std::string& prefix) -> Prebuilt<int, double, double>* {
150+ auto config = knncolle_annoy::load_annoy_prebuilt_types(prefix);
151+ if (config.index != knncolle::get_numeric_type<int >()) {
152+ throw std::runtime_error("unexpected type for the Annoy index");
153+ }
154+ if (config.data != knncolle::get_numeric_type<float >()) {
155+ throw std::runtime_error("unexpected type for the Annoy data");
156+ }
157+
158+ if (config.distance == "euclidean") {
159+ return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Euclidean>(prefix);
160+ } else if (config.distance == "manhattan") {
161+ return knncolle_annoy::load_annoy_prebuilt<int, double, double, Annoy::Manhattan>(prefix);
162+ } else {
163+ const auto path = prefix + ".custom_distance";
164+ const auto msg = knncolle::quick_load_as_string(path);
165+ if (msg != "hi this is a custom distance") {
166+ throw std::runtime_error("unknown custom distance");
167+ }
168+ return knncolle_annoy::load_annoy_prebuilt<int , double , double , MyCustomDistance>(prefix);
169+ }
170+ };
171+
172+ std::string custom_prefix = " annoy/location/custom_" ;
173+ knncolle_annoy::AnnoyBuilder<int , double , double , MyCustomDistance> custom_builder;
174+ auto custom_index = custom_builder.build_unique(mat);
175+ custom_index.save(custom_prefix);
176+ auto custom_reloaded = knncolle::load_prebuilt(custom_prefix);
177+ ```
178+
133179## Building projects
134180
135181### CMake with ` FetchContent `
0 commit comments