File:  [CENS] / python / pyGiNaC / wrappers / numeric.py
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Tue Apr 17 22:39:25 2001 UTC (16 years, 7 months ago) by pearu
Branches: MAIN
CVS tags: HEAD
Fixed bugs. Exposed/checked functions. Started testing framework.

# This file is part of the PyGiNaC package.
# http://cens.ioc.ee/projects/pyginac/
#
# $Revision: 1.4 $
# $Id: numeric.py,v 1.4 2001-04-17 22:39:25 pearu Exp $
#
# Copyright 2001 Pearu Peterson all rights reserved,
# Pearu Peterson <pearu@cens.ioc.ee>
# Permission to use, modify, and distribute this software is given under the
# terms of the LGPL.  See http://www.fsf.org
#
# NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
#
# TODO (help/contribution is appreciated):
# __long__, __complex__, numeric <-> Python string
# smod

depends = ['basic']

#STARTPROTO


class numeric(basic):
    """A wrapper around CLN-numbers within the GiNaC class hierarchy."""
    def __init__(self,numer = 0, denom = 1):
        """numeric() - construct CLN integer 0.
    numeric(i) - construct CLN integer from Python integer `i'.
    numeric(f) - construct CLN real from Python float `f'.
    numeric(c) - construct CLN number from Python complex `c'.
    numeric(s) - construct CLN number from Python string `s'.
    numeric(n,d) - construct CLN rational n/d from Python numbers `n,d'.
    numeric(ex(numeric(...))) - returns numeric(...).
    """
    def is_positive(self):
        """True if not complex and greater than zero."""
    def is_negative(self):
        """True if not complex and less than zero."""
    def is_integer(self):
        """True if a non-complex integer."""
    def is_pos_integer (self):
        """True if an exact integer greater than zero."""
    def is_nonneg_integer (self):
        """True if an exact integer greater or equal zero."""
    def is_even (self):
        """True if an exact even integer."""
    def is_odd (self):
        """True if an exact odd integer."""
    def is_prime (self):
        """Probabilistic primality test. True if an exact integer and prime."""
    def is_rational (self):
        """True if an exact rational number, may even be complex
    (denominator may be unity)."""
    def is_real (self):
        """True if a real integer, rational or float (but not complex)."""
    def is_cinteger (self):
        """True if element of the domain of integers extended by I, i.e.
    is of the form a+b*I, where a and b are integers."""
    def is_crational (self):
        """True if object is an exact rational number, may even
    be complex (denominator may be unity)."""
    def real (self):
        """Real part of a number."""
    def imag (self):
        """Imaginar part of a number."""
    def numer(self):
        """Numerator.
    Computes the numerator of rational numbers, rationalized numerator
    of complex if real and imaginary part are both rational numbers
    (i.e numer(4/3+5/6*I) == 8+5*I), the number carrying the sign in
    all other cases.
    """
    def denom(self):
        """Denominator.
    Computes the denominator of rational numbers, common integer
    denominator of complex if real and imaginary part are both
    rational numbers (i.e denom(4/3+5/6*I) == 6), one in all other
    cases.
    """
    def __int__(self):
        """Converts numeric types to Python's int."""
    def __float__(self):
        """Converts numeric types to Python's double."""
    def __len__(self):
        """Size in binary notation.
    For integers, this is the smallest n >= 0 such that -2^n <= x < 2^n.
    If x > 0, this is the unique n > 0 such that 2^(n-1) <= x < 2^n.
    Returns number of bits (excluding sign) needed to represent that
    number in two's complement if it is an integer, 0 otherwise.
    """
    def __cmp__(self, other):
        """A canonical order on all numbers.
    For complex numbers this is not possible in a mathematically
    consistent way but we need to establish some order and it ought to
    be fast. So we simply define it to be compatible with our method
    csgn.
    Returns csgn(self-other). 
    """
    def inverse(self):
        """Inverse of a number."""
    def csgn(self):
        """Return the complex half-plane (left or right) in which the
    number lies.
    csgn(x)==0 for x==0, csgn(x)==1 for Re(x)>0 or Re(x)=0 and
    Im(x)>0, csgn(x)==-1 for Re(x)<0 or Re(x)=0 and Im(x)<0.
    """
    def is_zero(self):
        """True if zero."""
    def is_equal(self, other):
        """True if equal to other."""
    def __power__(self, other):
        """Numerical exponentiation. [pow(b,e,m) not implemented]"""
    def __coerse__(self, other):
        """If ex(other)==ex(numeric(other)), then returns (self,numeric(other))
        Otherwise returns (ex(self), ex(other)).
        """
    def __add__(self,other):
        """Numerical addition."""
    def __sub__(self,other):
        """Numerical subtraction."""
    def __mul__(self,other):
        """Numerical multiplication."""
    def __div__(self,other):
        """Numerical division."""
    def __neg__(self):
        """Numerical negation."""
    def __pos__(self):
        """"""
class _Digits:
    """A global singleton object Digits which behaves just like Maple's
    Digits.
    """
    def __int__(self):
        """Convert to Python int."""
    def set(self,i):
        """Set value to Python integer `i'"""

Digits = '_Digits()'
I = 'Imaginary unit'

#ENDPROTO

#STARTDOCTEST
def test_02_numeric():
    """
This doc string tests CLN numeric objects
>>> from ginac import numeric

To constract CLN number from a Python int:
>>> numeric(2)
numeric('2')

... from a Python float:
>>> numeric(2.0)
numeric('2.0')

... from a Python complex:
>>> numeric(2.0+3.0j)
numeric('2.0+3.0*I')

... from a Python string:
>>> numeric('2.3')
numeric('2.3')

... from a numeric:
>>> numeric(numeric('2/3'))
numeric('2/3')

... from a pair of numbers:
>>> numeric(2,3)
numeric('2/3')
>>> numeric('2.3', 2)
numeric('1.15')
>>> numeric(1,numeric(3))
numeric('1/3')

... from an ex object:
>>> from ginac import ex,symbol
>>> numeric(ex(3))
numeric('3')
>>> numeric(ex(symbol()))
Traceback (most recent call last):
    ...
TypeError: ex_to_numeric() argument must be ex(numeric)
>>> numeric(symbol())
Traceback (most recent call last):
    ...
TypeError: numeric() argument must be int|float|complex|string|numeric|obj,obj|ex(numeric)

For constructing complex numbers, use predefined imaginary unit:
>>> from ginac import I
>>> I
numeric('I')
>>> 3*I+2
numeric('2+3*I')
>>> I/3
numeric('1/3*I')

Note that pretty printed ratonal numbers are in the form \d+/\d+ and therefore
not usable for evaluation as a Python string:
>>> print numeric(2)/3
2/3

For evaluation, apply first evalf() method or function to convert rational
numbers to floats:
>>> from ginac import evalf
>>> evalf(numeric(2)/3)
numeric('0.6666666666666666667')
>>> numeric('2/3').evalf()
numeric('0.6666666666666666667')

Use Digits object to change the number of digits in CLN numbers:
>>> from ginac import Digits
>>> Digits
Digits
>>> int(Digits)
17
>>> print Digits
17
>>> Digits.set(25)
>>> int(Digits)
25
>>> numeric(2.0)/3
numeric('0.66666666666666666666666666667')
>>> Digits.set(8)
>>> numeric(2.0)/3
numeric('0.6666666666666666')
>>> Digits.set(17) # back to default

CLN numeric objects can be transformed to Python numbers:
>>> int(numeric(3))
3
>>> int(numeric(3.4))
3
>>> long(numeric(3))   # not implemented yet
Traceback (most recent call last):
...
TypeError: object can't be converted to long
>>> float(numeric(3))
3.0
>>> float(numeric(3)+I)
3.0
>>> float(numeric(2)/3)
0.66666666666666663
>>> complex(numeric(3))
(3+0j)
>>> complex(numeric(3)+I)
(3+1j)

Real and imaginary parts:
>>> n = numeric('2/3-4/5*I')
>>> n
numeric('2/3-4/5*I')
>>> n.real()
numeric('2/3')
>>> n.imag()
numeric('-4/5')

numeric has other methods as well:
>>> n.inverse()
numeric('75/122+45/61*I')
>>> n.inverse()*n
numeric('1')
>>> n.csgn()
1
>>> (-n).csgn()
-1
>>> n.numer()
numeric('10-12*I')
>>> n.denom()
numeric('15')
>>> n.is_equal(n.numer()/n.denom())
1
>>> (n-n.numer()/n.denom()).is_zero()
1
>>> n.is_positive(),n.is_negative(),n.is_integer(),n.is_rational(),n.is_real()
(0, 0, 0, 0, 0)
>>> n.is_cinteger(),n.is_crational()
(0, 1)
>>> m = numeric(3)
>>> m.is_positive(),m.is_negative(),m.is_integer(),m.is_rational(),m.is_real()
(1, 0, 1, 1, 1)
>>> m.is_cinteger(),m.is_crational()
(1, 1)
>>> m.is_pos_integer(), m.is_nonneg_integer()
(1, 1)
>>> (-m).is_pos_integer(), (-m).is_nonneg_integer()
(0, 0)
>>> numeric().is_nonneg_integer()
1
>>> m.is_even(), m.is_odd(), m.is_prime()
(0, 1, 1)
>>> len(numeric(345))
9
>>> abs(numeric(-3))
numeric('3')

Unary operations:
>>> +n
numeric('2/3-4/5*I')
>>> -n
numeric('-2/3+4/5*I')

Binary operations:
>>> n+m
numeric('11/3-4/5*I')
>>> n-m
numeric('-7/3-4/5*I')
>>> n*m
numeric('2-12/5*I')
>>> n/m
numeric('2/9-4/15*I')
>>> n**m
numeric('-664/675-208/375*I')
>>> n**n
power(numeric('2/3-4/5*I'), numeric('2/3-4/5*I'))
>>> m**n
power(numeric('3'), numeric('2/3-4/5*I'))
>>> n+3
numeric('11/3-4/5*I')
>>> 3+n
numeric('11/3-4/5*I')
>>> n-3
numeric('-7/3-4/5*I')
>>> 3-n
numeric('7/3+4/5*I')
>>> n*3
numeric('2-12/5*I')
>>> 3*n
numeric('2-12/5*I')
>>> n/3
numeric('2/9-4/15*I')
>>> 3/n
numeric('225/122+135/61*I')
>>> n**3
numeric('-664/675-208/375*I')
>>> 3**n
power(numeric('3'), numeric('2/3-4/5*I'))


"""


#ENDDOCTEST
wrapperclass = '''

class numeric_w : public GiNaC::numeric {
  PyObject * self;
public:

  numeric_w(const GiNaC::numeric & other): GiNaC::numeric(other), self() {
    DEBUG_C("numeric_w::numeric_w(raw)");
  }
  numeric_w(python::ref);

  numeric_w(PyObject * self_): GiNaC::numeric(), self(self_) {
    DEBUG_C("numeric_w::numeric_w()");
  }
  numeric_w(PyObject * self_, const GiNaC::numeric & other)
    : GiNaC::numeric(other), self(self_) {
    DEBUG_C("numeric_w::numeric_w(numeric)");
  }
  numeric_w(PyObject * self_, const GiNaC::ex & other)
    : GiNaC::numeric(ex_to_numeric_w(other)), self(self_) {
    DEBUG_C("numeric_w::numeric_w(ex)");
  }
  numeric_w(PyObject * self_, python::ref other)
  : GiNaC::numeric(numeric_w(other)), self(self_) {
    DEBUG_C("numeric_w::numeric_w(ref)");
  }
  numeric_w::numeric_w(PyObject * self_, python::ref numer, python::ref denom)
  : GiNaC::numeric(numeric_w(numer)/numeric_w(denom)), self(self_) {
    DEBUG_C("numeric_w::numeric_w(ref,ref)");
  }
  ~numeric_w() {
    DEBUG_C("numeric_w::~numeric_w()");
  }

  long to_long_w(void) const {
    DEBUG_M("numeric.to_long()");
    return cln::cl_I_to_long(cln::truncate1(cln::the<cln::cl_R>(this->to_cl_N())));
  }
#ifdef PYGINAC_matrix
  UNEX_RET op_mul_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(other.mul(*this)));
  }
  UNEX_RET op_div_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(other.inverse().mul(*this)));
  }
#endif
};

class _numeric_digits_w : public GiNaC::_numeric_digits {
  PyObject * self;
public:
  _numeric_digits_w(PyObject * self_, const GiNaC::_numeric_digits & other)
    : GiNaC::_numeric_digits(other), self(self_) {}
  std::string python_repr(void) const {
    std::ostrstream os;
    os << "Digits" << std::ends;
    return os.str();
  }
  std::string python_str(void) const {
    std::ostrstream os;
    this -> print (os);
    os << std::ends;
    return os.str();
  }
  void set_w(long d) { ((GiNaC::_numeric_digits &)(*this)) = d; }
  long to_long_w(void) { return *this; }
};
'''

builder = '''
python::class_builder<numeric_w> numeric_w_class(this_module, "_numeric_w");
python::class_builder<GiNaC::numeric, numeric_w> numeric_class(this_module, "numeric");
numeric_py_class = python::as_object(numeric_class.get_extension_class());
numeric_class.declare_base(numeric_w_class);
numeric_class.declare_base(basic_class);

python::class_builder<_numeric_digits_w> _numeric_digits_w_class(this_module, "_numeric_digits_w");
python::class_builder<GiNaC::_numeric_digits, _numeric_digits_w> _numeric_digits_class(this_module, "_numeric_digits");
_numeric_digits_class.declare_base(_numeric_digits_w_class);
'''

constructors = '''
numeric_class.def(python::constructor<>());
numeric_class.def(python::constructor<const GiNaC::numeric &>());
numeric_class.def(python::constructor<const GiNaC::ex &>());
numeric_class.def(python::constructor<python::ref>());
numeric_class.def(python::constructor<python::ref, python::ref>());
'''

defs = '''
_numeric_digits_class.def(&_numeric_digits_w::python_repr, "__repr__");
_numeric_digits_class.def(&_numeric_digits_w::python_str, "__str__");
_numeric_digits_class.def(&_numeric_digits_w::to_long_w, "__int__");
_numeric_digits_class.def(&_numeric_digits_w::set_w, "set");
this_module.add(BOOST_PYTHON_CONVERSION::to_python(GiNaC::Digits), "Digits");
this_module.add(BOOST_PYTHON_CONVERSION::to_python(GiNaC::I), "I");

numeric_class.def(&basic_w::python_str, "__str__");
numeric_class.def(&basic_w::python_repr, "__repr__");
numeric_class.def(&numeric_coerce, "__coerce__");
numeric_class.def(python::operators<(python::op_abs)>());
#ifdef PYGINAC_matrix
numeric_class.def(&numeric_w::op_mul_matrix, "__mul__");
numeric_class.def(&numeric_w::op_div_matrix, "__div__");
#endif
BASIC_OPS(numeric)

//numeric_class.def(&numeric_w::add, "add");
//numeric_class.def(&numeric_w::sub, "sub");
//numeric_class.def(&numeric_w::mul, "mul");
//numeric_class.def(&numeric_w::div, "div");
//numeric_class.def(&numeric_w::power_w, "__pow__");
//numeric_class.def(&numeric_w::add_dyn, "add_dyn");
//numeric_class.def(&numeric_w::sub_dyn, "sub_dyn");
//numeric_class.def(&numeric_w::div_dyn, "div_dyn");
//numeric_class.def(&numeric_w::power_dyn, "power_dyn");
numeric_class.def(&numeric_w::inverse, "inverse");
numeric_class.def(&numeric_w::csgn, "csgn");
numeric_class.def(&numeric_w::compare, "__cmp__");
numeric_class.def(&numeric_w::is_positive, "is_positive");
numeric_class.def(&numeric_w::is_negative, "is_negative");
numeric_class.def(&numeric_w::is_integer, "is_integer");
numeric_class.def(&numeric_w::is_pos_integer, "is_pos_integer");
numeric_class.def(&numeric_w::is_nonneg_integer, "is_nonneg_integer");
numeric_class.def(&numeric_w::is_even, "is_even");
numeric_class.def(&numeric_w::is_odd, "is_odd");
numeric_class.def(&numeric_w::is_prime, "is_prime");
numeric_class.def(&numeric_w::is_rational, "is_rational");
numeric_class.def(&numeric_w::is_real, "is_real");
//numeric_class.def(&numeric_w::is_equal, "is_equal"); //defined in basic
numeric_class.def(&numeric_w::is_zero, "is_zero");
numeric_class.def(&numeric_w::is_cinteger, "is_cinteger");
numeric_class.def(&numeric_w::is_crational, "is_crational");
//numeric_class.def(&numeric_w::to_int, "to_int");
numeric_class.def(&numeric_w::to_long_w, "__int__");
numeric_class.def(&numeric_w::to_double, "__float__");
//numeric_class.def(&numeric_w::to_cl_N, "to_cl_N");
numeric_class.def(&numeric_w::real, "real");
numeric_class.def(&numeric_w::imag, "imag");
numeric_class.def(&numeric_w::numer, "numer"); //defined in basic
numeric_class.def(&numeric_w::denom, "denom"); //defined in basic
numeric_class.def(&numeric_w::int_length, "__len__");

'''

implementation = '''
EX_TO_BASIC(numeric)

static python::tuple numeric_coerce(const GiNaC::numeric & left, python::ref right) {
  GiNaC::ex r = ex_w(right);
  if (is_ex_exactly_of_type(r, numeric))
    return python::tuple(left,ex_to_numeric(r));
#ifdef PYGINAC_matrix
  if (is_ex_exactly_of_type(r, matrix))
    return python::tuple(left, ex_to_matrix(r));
#endif
  return python::tuple(GiNaC::ex(left), r);
}

numeric_w::numeric_w(python::ref other): GiNaC::numeric()
{
  PyObject * o = other.get();
  DEBUG_C("numeric_w::numeric_w(raw:ref)");
  if (PyInt_Check(o))
    this->GiNaC::numeric::operator=(PyInt_AS_LONG(o));
  else if (PyFloat_Check(o))
    this->GiNaC::numeric::operator=(PyFloat_AS_DOUBLE(o));
  else if (PyComplex_Check(o))
    this->GiNaC::numeric::operator=(GiNaC::numeric(
    cln::complex(PyComplex_RealAsDouble(o),PyComplex_ImagAsDouble(o))
    ));
  else if (PyString_Check(o))
    this->GiNaC::numeric::operator=(PyString_AS_STRING(o));
  else if (NumericInstance_Check(o))
    this->GiNaC::numeric::operator=(BOOST_PYTHON_CONVERSION::from_python(o, python::type<const GiNaC::numeric &>()));
  else {
    PyErr_SetString(PyExc_TypeError, "numeric() argument must be int|float|complex|string|numeric|obj,obj|ex(numeric)");
    throw python::error_already_set();
  }
}
'''

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>