Skip to content

Commit a8be5c9

Browse files
committed
add rowSums() and colSums() functions to Eidos
1 parent 1601c7d commit a8be5c9

7 files changed

Lines changed: 289 additions & 3 deletions

File tree

EidosScribe/EidosHelpFunctions.rtf

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,9 +4544,15 @@ The above explanation may not be entirely clear, so let\'92s look at an example.
45444544
\f3\fs20 , 2\'966, in the second column. Although visualization becomes more difficult, these same patterns extend to higher dimensions and arbitrary margins of
45454545
\f1\fs18 x
45464546
\f3\fs20 .\
4547+
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
4548+
\cf2 \kerning1\expnd0\expndtw0 For efficiently obtaining the sums of the rows or columns of a matrix, see
4549+
\f1\fs18 rowSums()
4550+
\f3\fs20 and
4551+
\f1\fs18 colSums()
4552+
\f3\fs20 .\
45474553
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
45484554

4549-
\f1\fs18 \cf0 \kerning1\expnd0\expndtw0 (*)array(*\'a0data, integer\'a0dim)
4555+
\f1\fs18 \cf0 (*)array(*\'a0data, integer\'a0dim)
45504556
\f2 \
45514557
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
45524558

@@ -4621,7 +4627,48 @@ To combine vectors or matrices by row instead, see
46214627
\f3\fs20 .\
46224628
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
46234629

4624-
\f1\fs18 \cf2 \kerning1\expnd0\expndtw0 (numeric$)det(numeric\'a0x)\
4630+
\f1\fs18 \cf2 \kerning1\expnd0\expndtw0 (numeric)colSums(lif\'a0x)\
4631+
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
4632+
4633+
\f0\b\fs20 \cf2 Returns the sums of the columns
4634+
\f3\b0 of
4635+
\f1\fs18 x
4636+
\f3\fs20 , which must be a matrix. The result is a vector of elements, each providing the sum of the corresponding column of
4637+
\f1\fs18 x
4638+
\f3\fs20 . If
4639+
\f1\fs18 x
4640+
\f3\fs20 is of type
4641+
\f1\fs18 logical
4642+
\f3\fs20 or
4643+
\f1\fs18 integer
4644+
\f3\fs20 the result will be of type
4645+
\f1\fs18 integer
4646+
\f3\fs20 ; unlike the
4647+
\f1\fs18 sum()
4648+
\f3\fs20 function,
4649+
\f1\fs18 colSums()
4650+
\f3\fs20 does not promote the return type to
4651+
\f1\fs18 float
4652+
\f3\fs20 if
4653+
\f1\fs18 integer
4654+
\f3\fs20 overflow occurs, but instead throws an error. If
4655+
\f1\fs18 x
4656+
\f3\fs20 is of type
4657+
\f1\fs18 float
4658+
\f3\fs20 the result will be of type
4659+
\f1\fs18 float
4660+
\f3\fs20 . Except for the change in the treatment of
4661+
\f1\fs18 integer
4662+
\f3\fs20 overflow noted above, this is equivalent to using
4663+
\f1\fs18 apply()
4664+
\f3\fs20 with
4665+
\f1\fs18 sum()
4666+
\f3\fs20 to sum the columns of
4667+
\f1\fs18 x
4668+
\f3\fs20 , but is much faster.\
4669+
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
4670+
4671+
\f1\fs18 \cf2 (numeric$)det(numeric\'a0x)\
46254672
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
46264673

46274674
\f0\b\fs20 \cf2 Returns the determinant
@@ -5029,7 +5076,48 @@ To combine vectors or matrices by column instead, see
50295076
\f3\fs20 .\
50305077
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
50315078

5032-
\f1\fs18 \cf0 \kerning1\expnd0\expndtw0 (*)t(*\'a0x)
5079+
\f1\fs18 \cf2 \kerning1\expnd0\expndtw0 (numeric)rowSums(lif\'a0x)\
5080+
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
5081+
5082+
\f0\b\fs20 \cf2 Returns the sums of the rows
5083+
\f3\b0 of
5084+
\f1\fs18 x
5085+
\f3\fs20 , which must be a matrix. The result is a vector of elements, each providing the sum of the corresponding row of
5086+
\f1\fs18 x
5087+
\f3\fs20 . If
5088+
\f1\fs18 x
5089+
\f3\fs20 is of type
5090+
\f1\fs18 logical
5091+
\f3\fs20 or
5092+
\f1\fs18 integer
5093+
\f3\fs20 the result will be of type
5094+
\f1\fs18 integer
5095+
\f3\fs20 ; unlike the
5096+
\f1\fs18 sum()
5097+
\f3\fs20 function,
5098+
\f1\fs18 rowSums()
5099+
\f3\fs20 does not promote the return type to
5100+
\f1\fs18 float
5101+
\f3\fs20 if
5102+
\f1\fs18 integer
5103+
\f3\fs20 overflow occurs, but instead throws an error. If
5104+
\f1\fs18 x
5105+
\f3\fs20 is of type
5106+
\f1\fs18 float
5107+
\f3\fs20 the result will be of type
5108+
\f1\fs18 float
5109+
\f3\fs20 . Except for the change in the treatment of
5110+
\f1\fs18 integer
5111+
\f3\fs20 overflow noted above, this is equivalent to using
5112+
\f1\fs18 apply()
5113+
\f3\fs20 with
5114+
\f1\fs18 sum()
5115+
\f3\fs20 to sum the rows of
5116+
\f1\fs18 x
5117+
\f3\fs20 , but is much faster.\
5118+
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
5119+
5120+
\f1\fs18 \cf0 (*)t(*\'a0x)
50335121
\f2 \
50345122
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
50355123

QtSLiM/help/EidosHelpFunctions.html

Lines changed: 5 additions & 0 deletions
Large diffs are not rendered by default.

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ development head (in the master branch):
1414
add rects() call to Plot, for plotting a set of rectangles
1515
extend text() to support drawing text and an angle, with new [float angle = 0.0] parameter
1616
add mtext() call to Plot, for drawing text in the margins outside the plot area
17+
add rowSums() and colSums() functions to Eidos, for use with matrices as a faster alternative to apply()
1718

1819

1920
version 5.1 (Eidos version 4.1):

eidos/eidos_functions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ const std::vector<EidosFunctionSignature_CSP> &EidosInterpreter::BuiltInFunction
297297
signatures->emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature("det", Eidos_ExecuteFunction_det, kEidosValueMaskNumeric | kEidosValueMaskSingleton))->AddNumeric("x"));
298298
signatures->emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature("inverse", Eidos_ExecuteFunction_inverse, kEidosValueMaskFloat))->AddNumeric("x"));
299299
signatures->emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature("asVector", Eidos_ExecuteFunction_asVector, kEidosValueMaskAny))->AddAny("x"));
300+
signatures->emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature("rowSums", Eidos_ExecuteFunction_rowSums, kEidosValueMaskNumeric))->AddLogicalEquiv("x"));
301+
signatures->emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature("colSums", Eidos_ExecuteFunction_colSums, kEidosValueMaskNumeric))->AddLogicalEquiv("x"));
300302

301303

302304
// ************************************************************************************

eidos/eidos_functions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ EidosValue_SP Eidos_ExecuteFunction_tr(const std::vector<EidosValue_SP> &p_argum
234234
EidosValue_SP Eidos_ExecuteFunction_det(const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
235235
EidosValue_SP Eidos_ExecuteFunction_inverse(const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
236236
EidosValue_SP Eidos_ExecuteFunction_asVector(const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
237+
EidosValue_SP Eidos_ExecuteFunction_rowSums(const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
238+
EidosValue_SP Eidos_ExecuteFunction_colSums(const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
237239

238240

239241
#pragma mark -

eidos/eidos_functions_matrices.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,178 @@ EidosValue_SP Eidos_ExecuteFunction_asVector(const std::vector<EidosValue_SP> &p
15371537
return result_SP;
15381538
}
15391539

1540+
// (numeric)rowSums(lif x)
1541+
EidosValue_SP Eidos_ExecuteFunction_rowSums(const std::vector<EidosValue_SP> &p_arguments, __attribute__((unused)) EidosInterpreter &p_interpreter)
1542+
{
1543+
EidosValue_SP result_SP(nullptr);
1544+
1545+
EidosValue *x_value = p_arguments[0].get();
1546+
EidosValueType x_type = x_value->Type();
1547+
1548+
if (x_value->DimensionCount() != 2)
1549+
EIDOS_TERMINATION << "ERROR (Eidos_ExecuteFunction_rowSums): in function rowSums() x is not a matrix." << EidosTerminate(nullptr);
1550+
1551+
const int64_t *dim_values = x_value->Dimensions();
1552+
size_t x_rowcount = (size_t)dim_values[0];
1553+
size_t x_colcount = (size_t)dim_values[1];
1554+
1555+
if (x_type == EidosValueType::kValueInt)
1556+
{
1557+
const int64_t *int_data = x_value->IntData();
1558+
1559+
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(x_rowcount);
1560+
result_SP = EidosValue_SP(int_result);
1561+
1562+
for (size_t value_index = 0; value_index < x_rowcount; ++value_index)
1563+
{
1564+
int64_t sum = 0;
1565+
const int64_t *series_ptr = int_data + value_index;
1566+
1567+
for (size_t col_index = 0; col_index < x_colcount; ++col_index)
1568+
{
1569+
// do sum += *series_ptr but check for overflow
1570+
int64_t old_sum = sum;
1571+
int64_t temp = *series_ptr;
1572+
bool overflow = Eidos_add_overflow(old_sum, temp, &sum);
1573+
1574+
if (overflow)
1575+
EIDOS_TERMINATION << "ERROR (Eidos_ExecuteFunction_rowSums): integer overflow in rowSums(); you might wish to convert to float before calling rowSums()." << EidosTerminate(nullptr);
1576+
1577+
series_ptr += x_rowcount;
1578+
}
1579+
1580+
int_result->set_int_no_check(sum, value_index);
1581+
}
1582+
}
1583+
else if (x_type == EidosValueType::kValueFloat)
1584+
{
1585+
const double *float_data = x_value->FloatData();
1586+
1587+
EidosValue_Float *float_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Float())->resize_no_initialize(x_rowcount);
1588+
result_SP = EidosValue_SP(float_result);
1589+
1590+
for (size_t value_index = 0; value_index < x_rowcount; ++value_index)
1591+
{
1592+
double sum = 0;
1593+
const double *series_ptr = float_data + value_index;
1594+
1595+
for (size_t col_index = 0; col_index < x_colcount; ++col_index)
1596+
{
1597+
sum += *series_ptr;
1598+
series_ptr += x_rowcount;
1599+
}
1600+
1601+
float_result->set_float_no_check(sum, value_index);
1602+
}
1603+
}
1604+
else if (x_type == EidosValueType::kValueLogical)
1605+
{
1606+
const eidos_logical_t *logical_data = x_value->LogicalData();
1607+
1608+
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(x_rowcount);
1609+
result_SP = EidosValue_SP(int_result);
1610+
1611+
for (size_t value_index = 0; value_index < x_rowcount; ++value_index)
1612+
{
1613+
int64_t sum = 0;
1614+
const eidos_logical_t *series_ptr = logical_data + value_index;
1615+
1616+
for (size_t col_index = 0; col_index < x_colcount; ++col_index)
1617+
{
1618+
sum += *series_ptr;
1619+
series_ptr += x_rowcount;
1620+
}
1621+
1622+
int_result->set_int_no_check(sum, value_index);
1623+
}
1624+
}
1625+
1626+
return result_SP;
1627+
}
1628+
1629+
// (numeric)colSums(lif x)
1630+
EidosValue_SP Eidos_ExecuteFunction_colSums(const std::vector<EidosValue_SP> &p_arguments, __attribute__((unused)) EidosInterpreter &p_interpreter)
1631+
{
1632+
EidosValue_SP result_SP(nullptr);
1633+
1634+
EidosValue *x_value = p_arguments[0].get();
1635+
EidosValueType x_type = x_value->Type();
1636+
1637+
if (x_value->DimensionCount() != 2)
1638+
EIDOS_TERMINATION << "ERROR (Eidos_ExecuteFunction_colSums): in function colSums() x is not a matrix." << EidosTerminate(nullptr);
1639+
1640+
const int64_t *dim_values = x_value->Dimensions();
1641+
size_t x_rowcount = (size_t)dim_values[0];
1642+
size_t x_colcount = (size_t)dim_values[1];
1643+
1644+
if (x_type == EidosValueType::kValueInt)
1645+
{
1646+
const int64_t *int_data = x_value->IntData();
1647+
1648+
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(x_colcount);
1649+
result_SP = EidosValue_SP(int_result);
1650+
1651+
for (size_t value_index = 0; value_index < x_colcount; ++value_index)
1652+
{
1653+
int64_t sum = 0;
1654+
const int64_t *series_ptr = int_data + value_index * x_rowcount;
1655+
1656+
for (size_t row_index = 0; row_index < x_rowcount; ++row_index)
1657+
{
1658+
// do sum += *series_ptr but check for overflow
1659+
int64_t old_sum = sum;
1660+
int64_t temp = *series_ptr;
1661+
bool overflow = Eidos_add_overflow(old_sum, temp, &sum);
1662+
1663+
if (overflow)
1664+
EIDOS_TERMINATION << "ERROR (Eidos_ExecuteFunction_colSums): integer overflow in colSums(); you might wish to convert to float before calling colSums()." << EidosTerminate(nullptr);
1665+
1666+
series_ptr++;
1667+
}
1668+
1669+
int_result->set_int_no_check(sum, value_index);
1670+
}
1671+
}
1672+
else if (x_type == EidosValueType::kValueFloat)
1673+
{
1674+
const double *float_data = x_value->FloatData();
1675+
1676+
EidosValue_Float *float_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Float())->resize_no_initialize(x_colcount);
1677+
result_SP = EidosValue_SP(float_result);
1678+
1679+
for (size_t value_index = 0; value_index < x_colcount; ++value_index)
1680+
{
1681+
double sum = 0;
1682+
const double *series_ptr = float_data + value_index * x_rowcount;
1683+
1684+
for (size_t row_index = 0; row_index < x_rowcount; ++row_index)
1685+
sum += *(series_ptr++);
1686+
1687+
float_result->set_float_no_check(sum, value_index);
1688+
}
1689+
}
1690+
else if (x_type == EidosValueType::kValueLogical)
1691+
{
1692+
const eidos_logical_t *logical_data = x_value->LogicalData();
1693+
1694+
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(x_colcount);
1695+
result_SP = EidosValue_SP(int_result);
1696+
1697+
for (size_t value_index = 0; value_index < x_colcount; ++value_index)
1698+
{
1699+
int64_t sum = 0;
1700+
const eidos_logical_t *series_ptr = logical_data + value_index * x_rowcount;
1701+
1702+
for (size_t row_index = 0; row_index < x_rowcount; ++row_index)
1703+
sum += *(series_ptr++);
1704+
1705+
int_result->set_int_no_check(sum, value_index);
1706+
}
1707+
}
1708+
1709+
return result_SP;
1710+
}
1711+
15401712

15411713

15421714

eidos/eidos_test_functions_other.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,22 @@ void _RunFunctionMatrixArrayTests(void)
370370
EidosAssertScriptSuccess_L("x = 1.0:12; y = matrix(x, nrow=3); identical(asVector(y), x);", true);
371371
EidosAssertScriptSuccess_L("x = (rbinom(12, 1, 0.5) == 1); y = matrix(x, nrow=3); identical(asVector(y), x);", true);
372372
EidosAssertScriptSuccess_L("x = c('a','b','c','d','e','f','g','h','i','j','k','l'); y = matrix(x, nrow=3); identical(asVector(y), x);", true);
373+
374+
// rowSums()
375+
EidosAssertScriptSuccess_L("x = c(T,T,F,F,T,F,F,T,T,F,F,T); y = matrix(x, nrow=3); identical(rowSums(y), c(1, 3, 2));", true);
376+
EidosAssertScriptSuccess_L("x = 1:12; y = matrix(x, nrow=3); identical(rowSums(y), c(22, 26, 30));", true);
377+
EidosAssertScriptSuccess_L("x = 1.0:12; y = matrix(x, nrow=3); identical(rowSums(y), c(22.0, 26, 30));", true);
378+
EidosAssertScriptSuccess_L("x = (rbinom(100, 1, 0.4) == 1); y = matrix(x, nrow=10); identical(rowSums(y), apply(y, 0, 'sum(applyValue);'));", true);
379+
EidosAssertScriptSuccess_L("x = rdunif(100, -1000, 1000); y = matrix(x, nrow=10); identical(rowSums(y), apply(y, 0, 'sum(applyValue);'));", true);
380+
EidosAssertScriptSuccess_L("x = runif(100); y = matrix(x, nrow=10); identical(rowSums(y), apply(y, 0, 'sum(applyValue);'));", true);
381+
382+
// colSums()
383+
EidosAssertScriptSuccess_L("x = c(T,T,F,F,T,F,F,T,T,F,F,T); y = matrix(x, nrow=3); identical(colSums(y), c(2, 1, 2, 1));", true);
384+
EidosAssertScriptSuccess_L("x = 1:12; y = matrix(x, nrow=3); identical(colSums(y), c(6, 15, 24, 33));", true);
385+
EidosAssertScriptSuccess_L("x = 1.0:12; y = matrix(x, nrow=3); identical(colSums(y), c(6.0, 15, 24, 33));", true);
386+
EidosAssertScriptSuccess_L("x = (rbinom(100, 1, 0.4) == 1); y = matrix(x, nrow=10); identical(colSums(y), apply(y, 1, 'sum(applyValue);'));", true);
387+
EidosAssertScriptSuccess_L("x = rdunif(100, -1000, 1000); y = matrix(x, nrow=10); identical(colSums(y), apply(y, 1, 'sum(applyValue);'));", true);
388+
EidosAssertScriptSuccess_L("x = runif(100); y = matrix(x, nrow=10); identical(colSums(y), apply(y, 1, 'sum(applyValue);'));", true);
373389
}
374390

375391
#pragma mark filesystem access

0 commit comments

Comments
 (0)