1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "lang_id/common/fel/feature-extractor.h"
18 
19 #include <string>
20 
21 #include "lang_id/common/fel/feature-types.h"
22 #include "lang_id/common/fel/fel-parser.h"
23 #include "lang_id/common/lite_base/logging.h"
24 #include "lang_id/common/lite_strings/numbers.h"
25 
26 namespace libtextclassifier3 {
27 namespace mobile {
28 
29 constexpr FeatureValue GenericFeatureFunction::kNone;
30 
GenericFeatureExtractor()31 GenericFeatureExtractor::GenericFeatureExtractor() {}
32 
~GenericFeatureExtractor()33 GenericFeatureExtractor::~GenericFeatureExtractor() {}
34 
Parse(const std::string & source)35 bool GenericFeatureExtractor::Parse(const std::string &source) {
36   // Parse feature specification into descriptor.
37   FELParser parser;
38 
39   if (!parser.Parse(source, mutable_descriptor())) {
40     SAFTM_LOG(ERROR) << "Error parsing the FEL spec " << source;
41     return false;
42   }
43 
44   // Initialize feature extractor from descriptor.
45   return InitializeFeatureFunctions();
46 }
47 
InitializeFeatureTypes()48 bool GenericFeatureExtractor::InitializeFeatureTypes() {
49   // Register all feature types.
50   GetFeatureTypes(&feature_types_);
51   for (size_t i = 0; i < feature_types_.size(); ++i) {
52     FeatureType *ft = feature_types_[i];
53     ft->set_base(i);
54 
55     // Check for feature space overflow.
56     double domain_size = ft->GetDomainSize();
57     if (domain_size < 0) {
58       SAFTM_LOG(ERROR) << "Illegal domain size for feature " << ft->name()
59                        << ": " << domain_size;
60       return false;
61     }
62   }
63   return true;
64 }
65 
GetParameter(const std::string & name,const std::string & default_value) const66 std::string GenericFeatureFunction::GetParameter(
67     const std::string &name, const std::string &default_value) const {
68   // Find named parameter in feature descriptor.
69   for (int i = 0; i < descriptor_->parameter_size(); ++i) {
70     if (name == descriptor_->parameter(i).name()) {
71       return descriptor_->parameter(i).value();
72     }
73   }
74   return default_value;
75 }
76 
GenericFeatureFunction()77 GenericFeatureFunction::GenericFeatureFunction() {}
78 
~GenericFeatureFunction()79 GenericFeatureFunction::~GenericFeatureFunction() { delete feature_type_; }
80 
GetIntParameter(const std::string & name,int default_value) const81 int GenericFeatureFunction::GetIntParameter(const std::string &name,
82                                             int default_value) const {
83   std::string value_str = GetParameter(name, "");
84   if (value_str.empty()) {
85     // Parameter not specified, use default value for it.
86     return default_value;
87   }
88   int value = 0;
89   if (!LiteAtoi(value_str, &value)) {
90     SAFTM_LOG(DFATAL) << "Unable to parse '" << value_str
91                       << "' as int for parameter " << name;
92     return default_value;
93   }
94   return value;
95 }
96 
GetBoolParameter(const std::string & name,bool default_value) const97 bool GenericFeatureFunction::GetBoolParameter(const std::string &name,
98                                               bool default_value) const {
99   std::string value = GetParameter(name, "");
100   if (value.empty()) return default_value;
101   if (value == "true") return true;
102   if (value == "false") return false;
103   SAFTM_LOG(DFATAL) << "Illegal value '" << value << "' for bool parameter "
104                     << name;
105   return default_value;
106 }
107 
GetFeatureTypes(std::vector<FeatureType * > * types) const108 void GenericFeatureFunction::GetFeatureTypes(
109     std::vector<FeatureType *> *types) const {
110   if (feature_type_ != nullptr) types->push_back(feature_type_);
111 }
112 
GetFeatureType() const113 FeatureType *GenericFeatureFunction::GetFeatureType() const {
114   // If a single feature type has been registered return it.
115   if (feature_type_ != nullptr) return feature_type_;
116 
117   // Get feature types for function.
118   std::vector<FeatureType *> types;
119   GetFeatureTypes(&types);
120 
121   // If there is exactly one feature type return this, else return null.
122   if (types.size() == 1) return types[0];
123   return nullptr;
124 }
125 
name() const126 std::string GenericFeatureFunction::name() const {
127   std::string output;
128   if (descriptor_->name().empty()) {
129     if (!prefix_.empty()) {
130       output.append(prefix_);
131       output.append(".");
132     }
133     ToFEL(*descriptor_, &output);
134   } else {
135     output = descriptor_->name();
136   }
137   return output;
138 }
139 
140 }  // namespace mobile
141 }  // namespace nlp_saft
142