LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
sym_number.h
Go to the documentation of this file.
1
/**
2
* @file
3
* @ingroup line_num
4
* An exact symbolic scalar, as a `num_traits` numeric type.
5
*
6
* Copyright (c) 2012-2026, Imperial College London
7
* All rights reserved.
8
*/
9
#ifndef LINE_NUM_SYM_NUMBER_H
10
#define LINE_NUM_SYM_NUMBER_H
11
12
#ifdef LINE_MP_USE_SYMENGINE
13
14
#include <cmath>
15
#include <limits>
16
#include <string>
17
18
#include <symengine/basic.h>
19
#include <symengine/eval_double.h>
20
#include <symengine/expression.h>
21
#include <symengine/symbol.h>
22
#include <symengine/visitor.h>
23
24
#include "
line/num/number.h
"
25
26
namespace
line
{
27
28
/**
29
* A symbolic scalar: a SymEngine expression carrying `+ - * /` and powers.
30
*
31
* WHY THIS IS A `num_traits` TYPE AND NOT A NEW CODE PATH. Every routine that
32
* is to accept symbolic input here is already `template <class T>` over
33
* `Matrix<T>`, and `num_traits<T>` is the one place a numeric type declares
34
* itself: `double`, `Rational`, `Real<D>` and `Complex` are already four such
35
* declarations. Adding a fifth makes `pfqn_gld` and `pfqn_gldsingle` symbolic
36
* WITHOUT EDITING EITHER OF THEM, which is also what keeps the symbolic and
37
* numeric arms from drifting: there is only one arm.
38
*
39
* `is_exact` is true and `has_transcendental` is FALSE, matching `Rational`.
40
* The second is what steers the gld family off its log-domain path, which is
41
* correct here for the reason it is correct there: the log domain exists to stop
42
* a double underflowing, and an exact expression cannot underflow. SymEngine
43
* itself does carry `log`, so unlike the JAR's Rings backend this type CAN
44
* represent `lG = log(G)`; `log_as_double` is nonetheless a double-valued
45
* accessor by the trait's contract, so it evaluates and is defined only for a
46
* fully substituted expression.
47
*
48
* COMPARISONS ARE STRUCTURAL, never decisions about the values a symbol may
49
* take. `operator==` compares normal forms; there is deliberately no
50
* `operator<`, because ordering a symbol has no meaning and the algorithms that
51
* would want one (the lld threshold scan) are the very ones the reference
52
* refuses on symbolic input.
53
*/
54
class
Sym {
55
public
:
56
Sym() : e_(SymEngine::integer(0)) {}
57
explicit
Sym(
const
SymEngine::Expression& e) : e_(e) {}
58
Sym(
int
v) : e_(SymEngine::integer(v)) {}
// NOLINT: implicit by design
59
Sym(
long
v) : e_(SymEngine::integer(v)) {}
// NOLINT
60
Sym(
const
SymEngine::RCP<const SymEngine::Basic>& b) : e_(b) {}
// NOLINT
61
62
/** A named symbol. */
63
static
Sym var(
const
std::string& name) {
64
return
Sym(SymEngine::Expression(SymEngine::symbol(name)));
65
}
66
67
/** A double carried in as the exact dyadic rational it already is. */
68
static
Sym from_double_exact(
double
v) {
69
return
Sym(SymEngine::Expression(SymEngine::real_double(v)));
70
}
71
72
const
SymEngine::Expression& expr()
const
{
return
e_; }
73
74
Sym operator+(
const
Sym& o)
const
{
return
Sym(e_ + o.e_); }
75
Sym operator-(
const
Sym& o)
const
{
return
Sym(e_ - o.e_); }
76
Sym operator*(
const
Sym& o)
const
{
return
Sym(e_ * o.e_); }
77
Sym operator/(
const
Sym& o)
const
{
return
Sym(e_ / o.e_); }
78
Sym operator-()
const
{
return
Sym(-e_); }
79
80
Sym& operator+=(
const
Sym& o) { e_ = e_ + o.e_;
return
*
this
; }
81
Sym& operator-=(
const
Sym& o) { e_ = e_ - o.e_;
return
*
this
; }
82
Sym& operator*=(
const
Sym& o) { e_ = e_ * o.e_;
return
*
this
; }
83
Sym& operator/=(
const
Sym& o) { e_ = e_ / o.e_;
return
*
this
; }
84
85
/** Structural equality of the normal form, not a claim about values. */
86
bool
operator==(
const
Sym& o)
const
{
return
e_ == o.e_; }
87
bool
operator!=(
const
Sym& o)
const
{
return
!(*
this
== o); }
88
89
std::string str()
const
{
return
SymEngine::str(e_); }
90
91
private
:
92
SymEngine::Expression e_;
93
};
94
95
inline
std::string to_string(
const
Sym& v) {
return
v.str(); }
96
97
template
<>
98
struct
num_traits
<Sym> {
99
using
type = Sym;
100
static
constexpr
bool
is_exact =
true
;
101
/**
102
* FALSE, as for `Rational`, and for the same reason: it is what steers a
103
* routine off its log-domain arm. SymEngine can represent `log`, but the
104
* trait means "may this type be handed to std::log and give a value of its
105
* own kind", and an expression cannot answer that without a substitution.
106
*/
107
static
constexpr
bool
has_transcendental =
false
;
108
static
const
char
* name() {
return
"symbolic"
; }
109
110
static
Sym from_int(
long
v) {
return
Sym(v); }
111
static
Sym from_rational(
long
num,
long
den) {
112
return
Sym(SymEngine::Expression(SymEngine::integer(num)) /
113
SymEngine::Expression(SymEngine::integer(den)));
114
}
115
static
Sym from_double(
double
v) {
return
Sym::from_double_exact(v); }
116
117
/**
118
* Defined only for a FULLY SUBSTITUTED expression: a symbol left in it has
119
* no double, and SymEngine raises rather than guessing one. That raise is
120
* the right behaviour for a caller who asked for a number.
121
*/
122
static
double
to_double(
const
Sym& v) {
123
return
SymEngine::eval_double(*v.expr().get_basic());
124
}
125
126
/**
127
* NaN ON A FREE SYMBOL, rather than a raise, and this is the one place the
128
* two differ.
129
*
130
* `NcResult<T>` carries `lG` as a plain double and every return path of the
131
* gld family fills it by calling this, unconditionally, on a value it has
132
* just computed. Under `T = Sym` that value is usually symbolic, so a raise
133
* here would make the SUCCESSFUL symbolic result unreachable: `G` is the
134
* answer and `lG` is a convenience beside it. NaN says "there is no double
135
* for this" in the field's own vocabulary and leaves `G` intact.
136
*
137
* A fully substituted expression still gets its real logarithm, so a caller
138
* who substituted before asking loses nothing.
139
*/
140
static
double
log_as_double(
const
Sym& v) {
141
if
(!SymEngine::free_symbols(*v.expr().get_basic()).empty()) {
142
return
std::numeric_limits<double>::quiet_NaN();
143
}
144
return
std::log(to_double(v));
145
}
146
static
std::string to_string(
const
Sym& v) {
return
v.str(); }
147
};
148
149
}
// namespace line
150
151
#endif
// LINE_MP_USE_SYMENGINE
152
#endif
// LINE_NUM_SYM_NUMBER_H
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
include
line
num
sym_number.h
Generated by
1.18.0