[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi] [PATCH 4/4] Implement loading of XML actuarial tables.
From: |
Vaclav Slavik |
Subject: |
[lmi] [PATCH 4/4] Implement loading of XML actuarial tables. |
Date: |
Thu, 17 May 2012 17:36:46 +0200 |
User-agent: |
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:12.0) Gecko/20120428 Thunderbird/12.0.1 |
Implement loading of XML actuarial tables.
This is only the first phase implementation where the XML file contains
only a single SOA table. It replaces binary tables with XML .xtable
files (created using a conversion tool I'll post in a moment). It does
not yet implement parallel use of both implementations.
Regards,
Vaclav
---
actuarial_table.cpp | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++-
actuarial_table.hpp | 52 +++++++-
2 files changed, 378 insertions(+), 7 deletions(-)
diff --git a/actuarial_table.cpp b/actuarial_table.cpp
index b875dac..ac7912f 100644
--- a/actuarial_table.cpp
+++ b/actuarial_table.cpp
@@ -33,6 +33,10 @@
#include "miscellany.hpp"
#include "oecumenic_enumerations.hpp" // methuselah
#include "path_utility.hpp" // fs::path inserter
+#include "value_cast.hpp"
+#include "xml_lmi.hpp"
+
+#include <xmlwrapp/nodes_view.h>
#include <boost/cstdint.hpp>
#include <boost/filesystem/convenience.hpp>
@@ -45,6 +49,7 @@
#include <climits> // CHAR_BIT
#include <ios>
#include <istream>
+#include <iterator> // std::distance()
#include <limits>
namespace
@@ -171,6 +176,324 @@ std::vector<double>
actuarial_table_base::values_elaborated
}
}
+actuarial_table::actuarial_table(std::string const& filename, int table_number)
+{
+ // TODO !! This is temporary code for API compatibility with
soa_actuarial_table.
+ // It should be changed so that the constructor takes only a single
+ // argument, filename of the XML table file.
+ std::string xmlfile(filename, 0, filename.rfind('.'));
+ xmlfile += "_";
+ xmlfile += value_cast<std::string>(table_number);
+ xmlfile += ".xtable";
+ load_xml_table(xmlfile);
+}
+
+actuarial_table::~actuarial_table()
+{
+}
+
+void actuarial_table::load_xml_table(std::string const& filename)
+{
+ xml_lmi::dom_parser parser(filename);
+ xml::element root(parser.root_node("table"));
+
+ // TODO !! Implement loading of multi-dimensional tables as well.
+
+ // XMLWRAPP !! This should be const_iterator, but xmlwrapp < 0.7 lacks the
+ // necessary operator!= for comparing const and non-const
iterators.
+ xml::node::iterator i;
+
+ if (root.end() != (i = root.find("aggregate")))
+ {
+ load_xml_aggregate_table(*i);
+ }
+ else if (root.end() != (i = root.find("duration")))
+ {
+ load_xml_duration_table(*i);
+ }
+ else if (root.end() != (i = root.find("select")))
+ {
+ load_xml_select_table(*i);
+ }
+ else if (root.end() != (i = root.find("select-and-ultimate")))
+ {
+ load_xml_select_and_ultimate_table(*i);
+ }
+ else
+ {
+ fatal_error()
+ << "Required data element not found."
+ << LMI_FLUSH
+ ;
+ }
+}
+
+void actuarial_table::load_xml_table_with_ages
+ (xml::element const& node
+ ,std::vector<double>& data
+ ,int& min_age
+ ,int& max_age
+ )
+{
+ xml::const_nodes_view const values = node.elements("value");
+
+ data.reserve(std::distance(values.begin(), values.end()));
+
+ min_age = max_age = -1;
+
+ typedef xml::const_nodes_view::const_iterator cnvi;
+ for(cnvi i = values.begin(); i != values.end(); ++i)
+ {
+ data.push_back(value_cast<double>(xml_lmi::get_content(*i)));
+ int age;
+ if(!xml_lmi::get_attr(*i, "age", age))
+ {
+ fatal_error()
+ << "XML <value> node doesn't have 'age' attribute."
+ << LMI_FLUSH
+ ;
+ }
+ if(-1 == min_age)
+ {
+ min_age = age;
+ }
+ else
+ {
+ if(max_age + 1 != age)
+ {
+ fatal_error()
+ << "XML <value> node has 'age' attribute value '"
+ << age
+ << "' but '"
+ << max_age + 1
+ << "' was expected."
+ << LMI_FLUSH
+ ;
+ }
+ }
+ max_age = age;
+ }
+
+ LMI_ASSERT(data.size() == size_t(max_age - min_age + 1));
+}
+
+void actuarial_table::load_xml_aggregate_table(xml::element const& node)
+{
+ load_xml_table_with_ages
+ (node
+ ,data_
+ ,min_age_
+ ,max_age_
+ );
+
+ table_type_ = e_table_aggregate;
+}
+
+void actuarial_table::load_xml_duration_table(xml::element const& node)
+{
+ xml::const_nodes_view const values = node.elements("value");
+
+ data_.reserve(std::distance(values.begin(), values.end()));
+
+ typedef xml::const_nodes_view::const_iterator cnvi;
+ for(cnvi i = values.begin(); i != values.end(); ++i)
+ {
+ data_.push_back(value_cast<double>(xml_lmi::get_content(*i)));
+ }
+
+ table_type_ = e_table_duration;
+}
+
+void actuarial_table::load_xml_select_table(xml::element const& node)
+{
+ xml::const_nodes_view const rows = node.elements("row");
+
+ if(!xml_lmi::get_attr(node, "period", select_period_))
+ {
+ fatal_error()
+ << "XML <select> node doesn't have 'period' attribute."
+ << LMI_FLUSH
+ ;
+ }
+
+ data_.reserve(std::distance(rows.begin(), rows.end()) * select_period_);
+
+ min_age_ = max_age_ = -1;
+
+ typedef xml::const_nodes_view::const_iterator cnvi;
+ for(cnvi i = rows.begin(); i != rows.end(); ++i)
+ {
+ int age;
+ if(!xml_lmi::get_attr(*i, "age", age))
+ {
+ fatal_error()
+ << "XML <row> node doesn't have 'age' attribute."
+ << LMI_FLUSH
+ ;
+ }
+ if(-1 == min_age_)
+ {
+ min_age_ = age;
+ }
+ else
+ {
+ if(max_age_ + 1 != age)
+ {
+ fatal_error()
+ << "XML <row> node has 'age' attribute value '"
+ << age
+ << "' but '"
+ << max_age_ + 1
+ << "' was expected."
+ << LMI_FLUSH
+ ;
+ }
+ }
+ max_age_ = age;
+
+ xml::const_nodes_view const values = i->elements("value");
+ if(select_period_ != std::distance(values.begin(), values.end()))
+ {
+ fatal_error()
+ << "XML <row> node has "
+ << std::distance(values.begin(), values.end())
+ << " values but select period is "
+ << select_period_
+ << "."
+ << LMI_FLUSH
+ ;
+ }
+
+ for(cnvi v = values.begin(); v != values.end(); ++v)
+ {
+ data_.push_back(value_cast<double>(xml_lmi::get_content(*v)));
+ }
+ }
+
+ max_select_age_ = max_age_;
+
+ LMI_ASSERT(data_.size() == size_t((max_age_ - min_age_ + 1) *
select_period_));
+
+ // Use the same type for select and select & ultimate tables: selected
+ // table is just a special case of the latter where max_age_ ==
+ // max_select_age_ and the ultimate table is empty.
+ table_type_ = e_table_select_and_ultimate;
+}
+
+void actuarial_table::load_xml_select_and_ultimate_table(xml::element const&
node)
+{
+ load_xml_select_table(*xml_lmi::retrieve_element(node, "select"));
+
+ int ultimate_min_age;
+ load_xml_table_with_ages
+ (*xml_lmi::retrieve_element(node, "ultimate")
+ ,ultimate_
+ ,ultimate_min_age
+ ,max_age_
+ );
+
+ if(ultimate_min_age != min_age_ + select_period_)
+ {
+ fatal_error()
+ << "Ultimate table should have min. age "
+ << min_age_
+ << ", but has "
+ << ultimate_min_age
+ << "."
+ << LMI_FLUSH
+ ;
+ }
+
+ table_type_ = e_table_select_and_ultimate;
+}
+
+
+std::vector<double> actuarial_table::specific_values
+ (int issue_age
+ ,int length
+ ) const
+{
+ if(table_type_ != e_table_duration)
+ {
+ // min_age_ and max_age_ are invalid for duration tables
+ LMI_ASSERT(min_age_ <= issue_age && issue_age <= max_age_);
+ LMI_ASSERT(0 <= length && length <= 1 + max_age_ - issue_age);
+ }
+
+ std::vector<double> v;
+ switch(table_type_)
+ {
+ case e_table_aggregate:
+ {
+ // Parenthesize the offsets--addition in C and C++ is
+ // in effect left associative:
+ // data_.begin() + issue_age - min_age_
+ // means
+ // (data_.begin() + issue_age) - min_age_
+ // but the subexpression
+ // (data_.begin() + issue_age)
+ // is likely to return a past-the-end iterator, which
+ // libstdc++'s debug mode will dislike.
+ //
+ v = std::vector<double>
+ (data_.begin() + (issue_age - min_age_)
+ ,data_.begin() + (issue_age - min_age_ + length)
+ );
+ }
+ break;
+ case e_table_duration:
+ {
+ v = std::vector<double>
+ (data_.begin()
+ ,data_.begin() + length
+ );
+ }
+ break;
+ case e_table_select_and_ultimate:
+ {
+ v.resize(length);
+ std::vector<double>::iterator cursor = v.begin();
+
+ int row_to_start_at;
+
+ // Write select portion:
+ if(issue_age < max_select_age_ + select_period_)
+ {
+ row_to_start_at = (std::min(max_select_age_, issue_age) -
min_age_);
+ int offset_in_row = std::max(0, issue_age - max_select_age_);
+ int k = offset_in_row
+ + row_to_start_at * select_period_
+ ;
+ for(int i = offset_in_row; cursor != v.end() && i <
select_period_; i++, k++)
+ *(cursor++) = data_[k];
+ }
+ else
+ {
+ const int min_ultimate_age = min_age_ + select_period_;
+ row_to_start_at = issue_age - min_ultimate_age;
+ }
+
+ // And ultimate:
+ for(int k = row_to_start_at; cursor != v.end(); k++)
+ *(cursor++) = ultimate_[k];
+ }
+ break;
+
+ default:
+ {
+ fatal_error()
+ << "Table type '"
+ << table_type_
+ << "' not recognized: must be one of 'A', 'D', or 'S'."
+ << LMI_FLUSH
+ ;
+ }
+ }
+ LMI_ASSERT(v.size() == static_cast<unsigned int>(length));
+ return v;
+}
+
+
soa_actuarial_table::soa_actuarial_table(std::string const& filename, int
table_number)
:filename_ (filename)
,table_number_ (table_number)
@@ -519,7 +842,7 @@ std::vector<double> soa_actuarial_table::specific_values
std::vector<double> v;
switch(table_type_)
{
- case 'A':
+ case e_table_aggregate:
{
// Parenthesize the offsets--addition in C and C++ is
// in effect left associative:
@@ -537,7 +860,7 @@ std::vector<double> soa_actuarial_table::specific_values
);
}
break;
- case 'D':
+ case e_table_duration:
{
v = std::vector<double>
(data_.begin()
@@ -545,7 +868,7 @@ std::vector<double> soa_actuarial_table::specific_values
);
}
break;
- case 'S':
+ case e_table_select_and_ultimate:
{
int const stride = 1 + select_period_;
int k =
@@ -587,7 +910,7 @@ std::vector<double> actuarial_table_rates
,int length
)
{
- soa_actuarial_table z(table_filename, table_number);
+ actuarial_table z(table_filename, table_number);
return z.values(issue_age, length);
}
@@ -601,7 +924,7 @@ std::vector<double> actuarial_table_rates_elaborated
,int reset_duration
)
{
- soa_actuarial_table z(table_filename, table_number);
+ actuarial_table z(table_filename, table_number);
return z.values_elaborated
(issue_age
,length
diff --git a/actuarial_table.hpp b/actuarial_table.hpp
index 94ec3e4..63b347f 100644
--- a/actuarial_table.hpp
+++ b/actuarial_table.hpp
@@ -28,6 +28,7 @@
#include "obstruct_slicing.hpp"
#include "uncopyable_lmi.hpp"
+#include "xml_lmi_fwd.hpp"
#include <iosfwd>
#include <string>
@@ -116,6 +117,16 @@ enum e_actuarial_table_method
};
+/// Types of actuarial tables.
+
+enum e_table_type
+ {e_table_invalid = -1
+ ,e_table_aggregate = 'A'
+ ,e_table_duration = 'D'
+ ,e_table_select_and_ultimate = 'S'
+ };
+
+
/// Base class for actuarial tables, both XML and binary.
// SOA !! This is only temporary, merge with xml_actuarial_table into
// single class once we remove binary SOA format support
@@ -150,6 +161,45 @@ class actuarial_table_base
int max_select_age_ ;
};
+/// Read actuarial table from XML file.
+
+class actuarial_table
+ : public actuarial_table_base
+ , private lmi::uncopyable <actuarial_table>
+ ,virtual private obstruct_slicing<actuarial_table>
+{
+ public:
+ actuarial_table(std::string const& filename, int table_number);
+ ~actuarial_table();
+
+ protected:
+ std::vector<double> specific_values(int issue_age, int length) const;
+
+ private:
+ void load_xml_table (std::string const& filename);
+ void load_xml_aggregate_table (xml::element const& node);
+ void load_xml_duration_table (xml::element const& node);
+ void load_xml_select_table (xml::element const& node);
+ void load_xml_select_and_ultimate_table(xml::element const& node);
+ void load_xml_table_with_ages (xml::element const& node
+ ,std::vector<double>& data
+ ,int& min_age
+ ,int& max_age
+ );
+
+ // Table data. For 1D tables (e_table_aggregate and e_table_duration), this
+ // is the vector of values from min_age_ to max_age_.
+ // For e_table_select_and_ultimate, the content is organized by rows, with
+ // select_period_ entries per row, with rows ranging from min_age_ to
+ // max_select_age_.
+ std::vector<double> data_;
+
+ // For e_table_select_and_ultimate, this vector contains the ultimate
+ // column. The first value, ultimate_[0], is for min_age_+select_period_,
+ // the last is for max_select_age_.
+ std::vector<double> ultimate_;
+};
+
/// Read a table from a database in the binary format designed by the
/// Society of Actuaries (SOA) and used for the tables SOA publishes.
///
@@ -196,8 +246,6 @@ class soa_actuarial_table
std::streampos table_offset_;
};
-typedef soa_actuarial_table actuarial_table;
-
/// Convenience function: read particular values from a table stored
/// in the SOA table-manager format.
--
1.7.10.2
- [lmi] [PATCH 4/4] Implement loading of XML actuarial tables.,
Vaclav Slavik <=
- Re: [lmi] Implement loading of XML actuarial tables., Vaclav Slavik, 2012/05/23
- [lmi] testing actuarial_table performance, Václav Slavík, 2012/05/26
- Re: [lmi] testing actuarial_table performance, Greg Chicares, 2012/05/27
- Re: [lmi] testing actuarial_table performance, Václav Slavík, 2012/05/28
- Re: [lmi] testing actuarial_table performance, Vaclav Slavik, 2012/05/29
- Re: [lmi] testing actuarial_table performance, Greg Chicares, 2012/05/30
- Re: [lmi] testing actuarial_table performance, Václav Slavík, 2012/05/30
- Re: [lmi] testing actuarial_table performance, Greg Chicares, 2012/05/31
- Re: [lmi] testing actuarial_table performance, Václav Slavík, 2012/05/31
Re: [lmi] Implement loading of XML actuarial tables., Greg Chicares, 2012/05/29