1 // OpenNN: Open Neural Networks Library
2 // www.opennn.net
3 //
4 // B O U N D I N G L A Y E R C L A S S
5 //
6 // Artificial Intelligence Techniques SL
7 // artelnics@artelnics.com
8
9 #include "bounding_layer.h"
10
11 namespace OpenNN
12 {
13
14 /// Default constructor.
15 /// It creates a bounding layer object with zero bounding neurons.
16
BoundingLayer()17 BoundingLayer::BoundingLayer() : Layer()
18 {
19 set();
20
21 set_default();
22 }
23
24
25 /// Bounding neurons number constructor.
26 /// It creates a bounding layer with a given size.
27 /// @param neurons_number Number of bounding neurons in the layer.
28
BoundingLayer(const Index & neurons_number)29 BoundingLayer::BoundingLayer(const Index& neurons_number) : Layer()
30 {
31 set(neurons_number);
32
33 set_default();
34 }
35
36
37 /// Destructor.
38 /// This destructor does not delete any pointer.
39
~BoundingLayer()40 BoundingLayer::~BoundingLayer()
41 {
42 }
43
44
45 /// Returns true if the size of the layer is zero, and false otherwise.
46
is_empty() const47 bool BoundingLayer::is_empty() const
48 {
49 if(get_neurons_number() == 0)
50 {
51 return true;
52 }
53 else
54 {
55 return false;
56 }
57 }
58
59
60 /// Returns the method used for bounding layer.
61
get_bounding_method() const62 const BoundingLayer::BoundingMethod& BoundingLayer::get_bounding_method() const
63 {
64 return bounding_method;
65 }
66
67 /// Returns a string writing if use bounding layer or not.
68
write_bounding_method() const69 string BoundingLayer::write_bounding_method() const
70 {
71 if(bounding_method == Bounding)
72 {
73 return "Bounding";
74 }
75 else if(bounding_method == NoBounding)
76 {
77 return "NoBounding";
78 }
79 else
80 {
81 ostringstream buffer;
82
83 buffer << "OpenNN Exception: BoundingLayer class.\n"
84 << "string write_bounding_method() const method.\n"
85 << "Unknown bounding method.\n";
86
87 throw logic_error(buffer.str());
88 }
89 }
90
91
92 /// Get number of inputs
93
get_inputs_number() const94 Index BoundingLayer::get_inputs_number() const
95 {
96 return lower_bounds.dimension(0);
97 }
98
99
100 /// Return the neurons number in the bounding layer.
101
get_neurons_number() const102 Index BoundingLayer::get_neurons_number() const
103 {
104 return lower_bounds.dimension(0);
105 }
106
107
108 /// Returns the lower bounds values of all the bounding neurons in the layer.
109
get_lower_bounds() const110 const Tensor<type, 1>& BoundingLayer::get_lower_bounds() const
111 {
112 return lower_bounds;
113 }
114
115
116 /// Returns the lower bound value of a single bounding neuron.
117 /// @param i Index of bounding neuron.
118
get_lower_bound(const Index & i) const119 type BoundingLayer::get_lower_bound(const Index& i) const
120 {
121
122
123 #ifdef __OPENNN_DEBUG__
124
125 const Index neurons_number = get_neurons_number();
126
127 if(i >= neurons_number)
128 {
129 ostringstream buffer;
130
131 buffer << "OpenNN Exception: BoundingLayer class.\n"
132 << "type get_lower_bound(const Index&) const method.\n"
133 << "Index must be less than number of bounding neurons.\n";
134
135 throw logic_error(buffer.str());
136 }
137
138 #endif
139
140 return lower_bounds[i];
141 }
142
143
144 /// Returns the upper bounds values of all the bounding neurons in the layer.
145
get_upper_bounds() const146 const Tensor<type, 1>& BoundingLayer::get_upper_bounds() const
147 {
148 return upper_bounds;
149 }
150
151
152 /// Returns the upper bound value of a single bounding neuron.
153 /// @param i Index of bounding neuron.
154
get_upper_bound(const Index & i) const155 type BoundingLayer::get_upper_bound(const Index& i) const
156 {
157 #ifdef __OPENNN_DEBUG__
158
159 const Index neurons_number = get_neurons_number();
160
161 if(neurons_number == 0)
162 {
163 ostringstream buffer;
164
165 buffer << "OpenNN Exception: BoundingLayer class.\n"
166 << "type get_upper_bound(const Index&) const method.\n"
167 << "Number of bounding neurons is zero.\n";
168
169 throw logic_error(buffer.str());
170 }
171 else if(i >= neurons_number)
172 {
173 ostringstream buffer;
174
175 buffer << "OpenNN Exception: BoundingLayer class.\n"
176 << "type get_upper_bound(const Index&) const method.\n"
177 << "Index must be less than number of bounding neurons.\n";
178
179 throw logic_error(buffer.str());
180 }
181
182 #endif
183
184 return upper_bounds(i);
185 }
186
187
188 /// Sets the number of bounding neurons to be zero.
189 /// It also sets the rest of memebers to their default values.
190
set()191 void BoundingLayer::set()
192 {
193 bounding_method = Bounding;
194
195 lower_bounds.resize(0);
196 upper_bounds.resize(0);
197
198 set_default();
199 }
200
201
202 /// Resizes the bounding layer.
203 /// It also sets the rest of memebers to their default values.
204 /// @param new_neurons_number Size of the bounding layer.
205
set(const Index & new_neurons_number)206 void BoundingLayer::set(const Index& new_neurons_number)
207 {
208 set_neurons_number(new_neurons_number);
209
210 set_default();
211 }
212
213
214 /// Resize the number of inputs.
215 /// @param new_inputs_number Size of the inputs array.
216
set_inputs_number(const Index & new_inputs_number)217 void BoundingLayer::set_inputs_number(const Index& new_inputs_number)
218 {
219 lower_bounds.resize(new_inputs_number);
220 upper_bounds.resize(new_inputs_number);
221 }
222
223
224 /// Resize the number of bound neurons from the bounding layer.
225 /// @param new_neurons_number Number of the neurons from the bounding layer.
226
set_neurons_number(const Index & new_neurons_number)227 void BoundingLayer::set_neurons_number(const Index& new_neurons_number)
228 {
229 lower_bounds.resize(new_neurons_number);
230 upper_bounds.resize(new_neurons_number);
231
232 lower_bounds.setConstant(-numeric_limits<type>::max());
233 upper_bounds.setConstant(numeric_limits<type>::max());
234 }
235
236
237 /// Sets the bounding layer members from a XML document.
238 /// @param bounding_layer_document Pointer to a TinyXML document containing the member data.
239
set(const tinyxml2::XMLDocument & bounding_layer_document)240 void BoundingLayer::set(const tinyxml2::XMLDocument& bounding_layer_document)
241 {
242 set_default();
243
244 from_XML(bounding_layer_document);
245 }
246
247
248 /// Sets the members of this object to be the members of another object of the same class.
249 /// @param other_bounding_layer Object to be copied.
250
set(const BoundingLayer & other_bounding_layer)251 void BoundingLayer::set(const BoundingLayer& other_bounding_layer)
252 {
253 lower_bounds = other_bounding_layer.lower_bounds;
254
255 upper_bounds = other_bounding_layer.upper_bounds;
256
257 display = other_bounding_layer.display;
258 }
259
260
261 /// Sets a new bounding method.
262 /// @param new_method New bounding method.
263
set_bounding_method(const BoundingMethod & new_method)264 void BoundingLayer::set_bounding_method(const BoundingMethod& new_method)
265 {
266 bounding_method = new_method;
267 }
268
269
270 /// Sets a new bounding method.
271 /// @param new_method_string New bounding method string.
272
set_bounding_method(const string & new_method_string)273 void BoundingLayer::set_bounding_method(const string& new_method_string)
274 {
275 if(new_method_string == "NoBounding")
276 {
277 bounding_method = NoBounding;
278 }
279 else if(new_method_string == "Bounding")
280 {
281 bounding_method = Bounding;
282 }
283 else
284 {
285 ostringstream buffer;
286
287 buffer << "OpenNN Exception: BoundingLayer class.\n"
288 << "void set_bounding_method(const string&) method.\n"
289 << "Unknown bounding method: " << new_method_string << ".\n";
290
291 throw logic_error(buffer.str());
292 }
293 }
294
295
296 /// Sets new lower bounds for all the neurons in the layer.
297 /// @param new_lower_bounds New set of lower bounds for the bounding neurons.
298
set_lower_bounds(const Tensor<type,1> & new_lower_bounds)299 void BoundingLayer::set_lower_bounds(const Tensor<type, 1>& new_lower_bounds)
300 {
301 #ifdef __OPENNN_DEBUG__
302
303 const Index neurons_number = get_neurons_number();
304
305 if(new_lower_bounds.size() != neurons_number)
306 {
307 ostringstream buffer;
308
309 buffer << "OpenNN Exception: BoundingLayer class.\n"
310 << "void set_lower_bounds(const Tensor<type, 1>&) method.\n"
311 << "Size must be equal to number of bounding neurons number.\n";
312
313 throw logic_error(buffer.str());
314 }
315
316 #endif
317
318 // Set lower bound of bounding neurons
319
320 lower_bounds = new_lower_bounds;
321 }
322
323
324 /// Sets a new lower bound for a single neuron.
325 /// This value is used for unscaling that variable so that it is not less than the lower bound.
326 /// @param index Index of bounding neuron.
327 /// @param new_lower_bound New lower bound for the neuron with index i.
328
set_lower_bound(const Index & index,const type & new_lower_bound)329 void BoundingLayer::set_lower_bound(const Index& index, const type& new_lower_bound)
330 {
331 const Index neurons_number = get_neurons_number();
332
333 #ifdef __OPENNN_DEBUG__
334
335 if(index >= neurons_number)
336 {
337 ostringstream buffer;
338
339 buffer << "OpenNN Exception: BoundingLayer class.\n"
340 << "void set_lower_bound(const Index&, const type&) method.\n"
341 << "Index of bounding neurons must be less than number of bounding neurons.\n";
342
343 throw logic_error(buffer.str());
344 }
345
346 #endif
347
348 if(lower_bounds.size() != neurons_number)
349 {
350 lower_bounds.resize(neurons_number);
351 lower_bounds.setConstant(-numeric_limits<type>::max());
352 }
353
354 // Set lower bound of single neuron
355
356 lower_bounds[index] = new_lower_bound;
357 }
358
359
360 /// Sets new upper bounds for all the bounding neurons.
361 /// These values are used for unscaling variables so that they are not greater than the upper bounds.
362 /// @param new_upper_bounds New set of upper bounds for the layer.
363
set_upper_bounds(const Tensor<type,1> & new_upper_bounds)364 void BoundingLayer::set_upper_bounds(const Tensor<type, 1>& new_upper_bounds)
365 {
366
367
368 #ifdef __OPENNN_DEBUG__
369
370 const Index neurons_number = get_neurons_number();
371
372 if(new_upper_bounds.size() != neurons_number)
373 {
374 ostringstream buffer;
375
376 buffer << "OpenNN Exception: BoundingLayer class.\n"
377 << "void set_upper_bound(const Tensor<type, 1>&) method.\n"
378 << "Size must be equal to number of bounding neurons.\n";
379
380 throw logic_error(buffer.str());
381 }
382
383 #endif
384
385 // Set upper bound of neurons
386
387 upper_bounds = new_upper_bounds;
388 }
389
390
391 /// Sets a new upper bound for a single neuron.
392 /// This value is used for unscaling that variable so that it is not greater than the upper bound.
393 /// @param index Index of bounding neuron.
394 /// @param new_upper_bound New upper bound for the bounding neuron with that index.
395
set_upper_bound(const Index & index,const type & new_upper_bound)396 void BoundingLayer::set_upper_bound(const Index& index, const type& new_upper_bound)
397 {
398 const Index neurons_number = get_neurons_number();
399
400
401
402 #ifdef __OPENNN_DEBUG__
403
404 if(index >= neurons_number)
405 {
406 ostringstream buffer;
407
408 buffer << "OpenNN Exception: BoundingLayer class.\n"
409 << "void set_upper_bound(const Index&, const type&) method.\n"
410 << "Index of bounding neuron must be less than number of bounding neurons.\n";
411
412 throw logic_error(buffer.str());
413 }
414
415 #endif
416
417 if(upper_bounds.size() != neurons_number)
418 {
419 upper_bounds.resize(neurons_number);
420 upper_bounds.setConstant(numeric_limits<type>::max());
421 }
422
423 upper_bounds[index] = new_upper_bound;
424
425 }
426
427
428 /// Sets a new display value.
429 /// If it is set to true messages from this class are to be displayed on the screen;
430 /// if it is set to false messages from this class are not to be displayed on the screen.
431 /// @param new_display Display value.
432
set_display(const bool & new_display)433 void BoundingLayer::set_display(const bool& new_display)
434 {
435 display = new_display;
436 }
437
438
439 /// Sets the members to their default values:
440 /// <ul>
441 /// <li> Display: True.
442 /// </ul>
443
set_default()444 void BoundingLayer::set_default()
445 {
446 layer_name = "bounding_layer";
447
448 bounding_method = Bounding;
449
450 layer_type = Layer::Bounding;
451 }
452
453
454 /// Calculates the outputs from the bounding layer for a set of inputs to that layer.
455 /// @param inputs Set of inputs to the bounding layer.
456
calculate_outputs(const Tensor<type,2> & inputs)457 Tensor<type, 2> BoundingLayer::calculate_outputs(const Tensor<type, 2>& inputs)
458 {
459 #ifdef __OPENNN_DEBUG__
460
461 const Index inputs_dimensions_number = inputs.rank();
462
463 if(inputs_dimensions_number != 2)
464 {
465 ostringstream buffer;
466
467 buffer << "OpenNN Exception: BoundingLayer class.\n"
468 << "Tensor<type, 2> calculate_outputs(const Tensor<type, 2>&) const method.\n"
469 << "Number of dimensions (" << inputs_dimensions_number << ") must be 2.\n";
470
471 throw logic_error(buffer.str());
472 }
473
474 const Index inputs_number = get_inputs_number();
475
476 const Index inputs_columns_number = inputs.dimension(1);
477
478 if(inputs_columns_number != inputs_number)
479 {
480 ostringstream buffer;
481
482 buffer << "OpenNN Exception: BoundingLayer class.\n"
483 << "Tensor<type, 2> calculate_outputs(const Tensor<type, 2>&) const method.\n"
484 << "Number of columns ("
485 << inputs_columns_number << ") must be equal to number of inputs ("
486 << inputs_number << ").\n";
487
488 throw logic_error(buffer.str());
489 }
490
491 #endif
492
493 switch(bounding_method)
494 {
495 case NoBounding: return inputs;
496
497 case Bounding:
498 {
499 const Index rows_number = inputs.dimension(0);
500 const Index columns_number = inputs.dimension(1);
501
502 Tensor<type, 2> outputs(rows_number, columns_number);
503
504 for(Index i = 0; i < rows_number; i++)
505 {
506 for(Index j = 0; j < columns_number; j++)
507 {
508 if(inputs(i,j) < lower_bounds(j)) outputs(i,j) = lower_bounds(j);
509 else if(inputs(i,j) > upper_bounds(j)) outputs(i,j) = upper_bounds(j);
510 else outputs(i,j) = inputs(i,j);
511 }
512 }
513
514 return outputs;
515 }
516 }
517
518 return Tensor<type, 2>();
519 }
520
521
522 /// Returns a string with the expression of the lower and upper bounds functions.
523
write_expression(const Tensor<string,1> & inputs_names,const Tensor<string,1> & outputs_names) const524 string BoundingLayer::write_expression(const Tensor<string, 1>& inputs_names, const Tensor<string, 1>& outputs_names) const
525 {
526 ostringstream buffer;
527
528 buffer.precision(10);
529
530 if(bounding_method == Bounding)
531 {
532 const Index neurons_number = get_neurons_number();
533
534 for(Index i = 0; i < neurons_number; i++)
535 {
536 buffer << outputs_names[i] << " = max(" << lower_bounds[i] << ", " << inputs_names[i] << ")\n";
537 buffer << outputs_names[i] << " = min(" << upper_bounds[i] << ", " << inputs_names[i] << ")\n";
538 }
539 }
540 else
541 {
542 buffer << "";
543 }
544
545 return buffer.str();
546 }
547
548
549 ///
550 /// \brief BoundingLayer::write_expression_c
551 /// \return
552
write_expression_c() const553 string BoundingLayer::write_expression_c() const
554 {
555 const Index neurons_number = get_neurons_number();
556
557 ostringstream buffer;
558
559 buffer << "vector<float> " << layer_name << "(const vector<float>& inputs)\n{" << endl;
560
561 buffer << "\tvector<float> outputs(" << neurons_number << ");\n" << endl;
562
563 for(Index i = 0; i < neurons_number; i++)
564 {
565 buffer << "\toutputs[" << i << "] = inputs[" << i << "];" << endl;
566 }
567
568 buffer << "\n\treturn outputs;\n}" << endl;
569
570 return buffer.str();
571 }
572
573
574 ///
575 /// \brief BoundingLayer::write_expression_python
576 /// \return
577
write_expression_python() const578 string BoundingLayer::write_expression_python() const
579 {
580 const Index neurons_number = get_neurons_number();
581
582 ostringstream buffer;
583
584 buffer << "def " << layer_name << "(inputs):\n" << endl;
585
586 buffer << "\toutputs = [None] * "<<neurons_number<<"\n" << endl;
587
588 for(Index i = 0; i < neurons_number; i++)
589 {
590 buffer << "\toutputs[" << i << "] = inputs[" << i << "]" << endl;
591 }
592
593 buffer << "\n\treturn outputs\n" << endl;
594
595 return buffer.str();
596 }
597
598
599 /// Serializes the bounding layer object into a XML document of the TinyXML library without keep the DOM tree in memory.
600 /// See the OpenNN manual for more information about the format of this document.
601
write_XML(tinyxml2::XMLPrinter & file_stream) const602 void BoundingLayer::write_XML(tinyxml2::XMLPrinter& file_stream) const
603 {
604 ostringstream buffer;
605
606 file_stream.OpenElement("BoundingLayer");
607
608 // Bounding neurons number
609
610 file_stream.OpenElement("BoundingNeuronsNumber");
611
612 const Index neurons_number = get_neurons_number();
613
614 buffer.str("");
615 buffer << neurons_number;
616
617 file_stream.PushText(buffer.str().c_str());
618
619 file_stream.CloseElement();
620
621 for(Index i = 0; i < neurons_number; i++)
622 {
623 file_stream.OpenElement("Item");
624
625 file_stream.PushAttribute("Index",static_cast<unsigned>(i+1));
626
627 // Lower bound
628
629 file_stream.OpenElement("LowerBound");
630
631 buffer.str("");
632 buffer << lower_bounds[i];
633
634 file_stream.PushText(buffer.str().c_str());
635
636 file_stream.CloseElement();
637
638 // Upper bound
639
640 file_stream.OpenElement("UpperBound");
641
642 buffer.str("");
643 buffer << upper_bounds[i];
644
645 file_stream.PushText(buffer.str().c_str());
646
647 file_stream.CloseElement();
648
649
650 file_stream.CloseElement();
651 }
652
653 // Bounding method
654
655 file_stream.OpenElement("UseBoundingLayer");
656
657 if(bounding_method == Bounding)
658 {
659 buffer.str("");
660 buffer << 1;
661 }
662 else if(bounding_method == NoBounding)
663 {
664 buffer.str("");
665 buffer << 0;
666 }
667 else
668 {
669 file_stream.CloseElement();
670
671 buffer << "OpenNN Exception: BoundingLayer class.\n"
672 << "void write_XML(tinyxml2::XMLPrinter&) const method.\n"
673 << "Unknown bounding method type.\n";
674
675 throw logic_error(buffer.str());
676 }
677
678 file_stream.PushText(buffer.str().c_str());
679
680 file_stream.CloseElement();
681
682 // // Display
683
684 // {
685 // file_stream.OpenElement("Display");
686
687 // buffer.str("");
688 // buffer << display;
689
690 // file_stream.PushText(buffer.str().c_str());
691
692 // file_stream.CloseElement();
693 // }
694
695 file_stream.CloseElement();
696 }
697
698
699 /// Deserializes a TinyXML document into this bounding layer object.
700 /// @param document TinyXML document containing the member data.
701
from_XML(const tinyxml2::XMLDocument & document)702 void BoundingLayer::from_XML(const tinyxml2::XMLDocument& document)
703 {
704 ostringstream buffer;
705
706 const tinyxml2::XMLElement* bounding_layer_element = document.FirstChildElement("BoundingLayer");
707
708 if(!bounding_layer_element)
709 {
710 buffer << "OpenNN Exception: BoundingLayer class.\n"
711 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
712 << "BoundingLayer element is nullptr.\n";
713
714 throw logic_error(buffer.str());
715 }
716
717 // Bounding neurons number
718
719 const tinyxml2::XMLElement* neurons_number_element = bounding_layer_element->FirstChildElement("BoundingNeuronsNumber");
720
721 if(!neurons_number_element)
722 {
723 buffer << "OpenNN Exception: BoundingLayer class.\n"
724 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
725 << "BoundingNeuronsNumber element is nullptr.\n";
726
727 throw logic_error(buffer.str());
728 }
729
730 const Index neurons_number = static_cast<Index>(atoi(neurons_number_element->GetText()));
731
732 set(neurons_number);
733
734 unsigned index = 0; // Index does not work
735
736 if(neurons_number > 0)
737 {
738 const tinyxml2::XMLElement* start_element = neurons_number_element;
739
740 for(Index i = 0; i < lower_bounds.size(); i++)
741 {
742 const tinyxml2::XMLElement* item_element = start_element->NextSiblingElement("Item");
743 start_element = item_element;
744
745 if(!item_element)
746 {
747 buffer << "OpenNN Exception: BoundingLayer class.\n"
748 << "void from_XML(const tinyxml2::XMLElement*) method.\n"
749 << "Item " << i+1 << " is nullptr.\n";
750
751 throw logic_error(buffer.str());
752 }
753
754 item_element->QueryUnsignedAttribute("Index", &index);
755
756 if(index != i+1)
757 {
758 buffer << "OpenNN Exception: BoundingLayer class.\n"
759 << "void from_XML(const tinyxml2::XMLElement*) method.\n"
760 << "Index " << index << " is not correct.\n";
761
762 throw logic_error(buffer.str());
763 }
764
765 // Lower bound
766
767 const tinyxml2::XMLElement* lower_bound_element = item_element->FirstChildElement("LowerBound");
768
769 if(lower_bound_element)
770 {
771 if(lower_bound_element->GetText())
772 {
773 lower_bounds[index-1] = static_cast<type>(atof(lower_bound_element->GetText()));
774 }
775 }
776
777 // Upper bound
778
779 const tinyxml2::XMLElement* upper_bound_element = item_element->FirstChildElement("UpperBound");
780
781 if(upper_bound_element)
782 {
783 if(upper_bound_element->GetText())
784 {
785 upper_bounds[index-1] = static_cast<type>(atof(upper_bound_element->GetText()));
786 }
787 }
788 }
789 }
790
791 // Use bounding layer
792 {
793 const tinyxml2::XMLElement* use_bounding_layer_element = bounding_layer_element->FirstChildElement("UseBoundingLayer");
794
795 if(use_bounding_layer_element)
796 {
797 Index new_method = static_cast<Index>(atoi(use_bounding_layer_element->GetText()));
798
799 if(new_method == 1)
800 {
801 bounding_method = Bounding;
802 }
803 else if(new_method == 0)
804 {
805 bounding_method = NoBounding;
806 }
807 else
808 {
809 buffer << "OpenNN Exception: BoundingLayer class.\n"
810 << "void from_XML(const tinyxml2::XMLElement*) method.\n"
811 << "Unknown bounding method.\n";
812
813 throw logic_error(buffer.str());
814 }
815 }
816 }
817 }
818
819 }
820
821 // OpenNN: Open Neural Networks Library.
822 // Copyright(C) 2005-2020 Artificial Intelligence Techniques, SL.
823 //
824 // This library is free software; you can redistribute it and/or
825 // modify it under the terms of the GNU Lesser General Public
826 // License as published by the Free Software Foundation; either
827 // version 2.1 of the License, or any later version.
828 //
829 // This library is distributed in the hope that it will be useful,
830 // but WITHOUT ANY WARRANTY; without even the implied warranty of
831 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
832 // Lesser General Public License for more details.
833
834 // You should have received a copy of the GNU Lesser General Public
835 // License along with this library; if not, write to the Free Software
836 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
837