/** * Convert if possible a supplied argument to a rational * * @param int|float|string|NumericTypeInterface $value * * @return \Chippyash\Math\Matrix\RationalNumber * * @throws \Chippyash\Matrix\Exceptions\MatrixException * @throws \Exception */ protected function convertNumberToRational($value) { if ($value instanceof NumericTypeInterface) { return $value->asRational(); } switch (gettype($value)) { case 'integer': return RationalTypeFactory::create($value, 1); case 'double': return RationalTypeFactory::fromFloat($value); case 'string': try { return RationalTypeFactory::fromString($value); } catch (\Exception $e) { try { return ComplexTypeFactory::fromString($value)->asRational(); } catch (\Exception $ex) { throw new MatrixException('The string representation of the number is invalid for a rational'); } } case 'NULL': return RationalTypeFactory::create(0, 1); case 'boolean': return RationalTypeFactory::create($value ? 1 : 0, 1); default: throw new MatrixException('Rational expects int, float, string, Rational or NumericTypeInterface '); } }
/** * @dataProvider polars */ public function testCreateFromPolarReturnsComplexType($r, $t) { $radius = RationalTypeFactory::fromString($r); $theta = RationalTypeFactory::fromString($t); $p = ComplexTypeFactory::fromPolar($radius, $theta); $this->assertInstanceOf(self::CTYPE_NAME, $p); }
public function testIntPowReturnsRationalOrIntOrComplexTypes() { $base = new IntType(5); $exp = new IntType(3); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\IntType', $this->object->intPow($base, $exp)); $this->assertEquals(125, $this->object->intPow($base, $exp)->get()); $exp2 = new FloatType(3.5); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Rational\\RationalType', $this->object->intPow($base, $exp2)); $this->assertEquals('332922571/1191100', (string) $this->object->intPow($base, $exp2)); $exp3 = RationalTypeFactory::fromFloat(3.5); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Rational\\RationalType', $this->object->intPow($base, $exp3)); $this->assertEquals('332922571/1191100', (string) $this->object->intPow($base, $exp3)); $base2 = new IntType(4); $exp4 = ComplexTypeFactory::fromString('5+3i'); $pow = $this->object->intPow($base2, $exp4); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Complex\\ComplexType', $pow); $this->assertEquals('-778299179/1445876', (string) $pow->r()); $this->assertEquals('-861158767/988584', (string) $pow->i()); $exp5 = ComplexTypeFactory::fromString('0+3i'); $pow = $this->object->intPow($base2, $exp5); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Complex\\ComplexType', $pow); $this->assertEquals('-1722722/3277175', (string) $pow->r()); $this->assertEquals('-20905959/24575389', (string) $pow->i()); $exp6 = ComplexTypeFactory::fromString('5+0i'); $pow = $this->object->intPow($base2, $exp6); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Rational\\RationalType', $pow); $this->assertEquals(1024, $pow->get()); }
/** * Construct a complete Matrix with all entries set to a complex number * Takes a source matrix or array (which can be incomplete and converts each * entry to complex number type, setting a default value if entry does not exist. * * If a Matrix is supplied as $source, the data is cloned into the ComplexMatrix * converting to complex number values, with no further checks, although you * may get exceptions thrown if conversion is not possible. * * If you don't supply a default value, then 0+0i will be used * * @param Matrix|array $source Array to initialise the matrix with * @param ComplexType $normalizeDefault Value to set missing vertices * */ public function __construct($source, ComplexType $normalizeDefault = null) { if (is_null($normalizeDefault)) { $ri = RationalTypeFactory::create(0, 1); $normalizeDefault = ComplexTypeFactory::create($ri, clone $ri); } parent::__construct($source, $normalizeDefault); }
public function testComputeReturnsCorrectResult() { $testArray = [[1, 2, 3], [3, 2, 1], [2, 1, 3]]; $expectedArray = [[RationalTypeFactory::create(3), RationalTypeFactory::create(4), RationalTypeFactory::create(5)], [RationalTypeFactory::create(5), RationalTypeFactory::create(4), RationalTypeFactory::create(3)], [RationalTypeFactory::create(4), RationalTypeFactory::create(3), RationalTypeFactory::create(5)]]; $object = new RationalMatrix($testArray); $computation = new \Chippyash\Math\Matrix\Computation\Add\Scalar(); $this->assertEquals($expectedArray, $object->compute($computation, 2)->toArray()); }
function createMatrix($size) { $c = 0; $fn = function ($r, $c) use(&$c) { return RationalTypeFactory::create($c++, 1); }; $iSize = TypeFactory::createInt($size); return MatrixFactory::createFromFunction($fn, $iSize, $iSize, new StringType('rational')); }
/** * Create a new scalar based on type of original matrix * * @param \Chippyash\Math\Matrix\NumericMatrix $originalMatrix * @param scalar $scalar * @return Chippyash\Type\Interfaces\NumericTypeInterface * */ protected function createCorrectScalarType(NumericMatrix $originalMatrix, $scalar) { if ($scalar instanceof NumericTypeInterface) { if ($originalMatrix instanceof RationalMatrix) { return $scalar->asRational(); } if ($originalMatrix instanceof ComplexMatrix) { return $scalar->asComplex(); } return $scalar; } if ($originalMatrix instanceof ComplexMatrix) { if (is_numeric($scalar)) { return ComplexTypeFactory::create($scalar, 0); } if (is_string($scalar)) { try { return RationalTypeFactory::create($scalar)->asComplex(); } catch (\Exception $e) { //do nothing } } if (is_bool($scalar)) { return ComplexTypeFactory::create($scalar ? 1 : 0, 0); } return ComplexTypeFactory::create($scalar); } if ($originalMatrix instanceof RationalMatrix) { if (is_bool($scalar)) { $scalar = $scalar ? 1 : 0; } return RationalTypeFactory::create($scalar); } //handling for NumericMatrix if (is_int($scalar)) { return TypeFactory::createInt($scalar); } elseif (is_float($scalar)) { return TypeFactory::createRational($scalar); } elseif (is_bool($scalar)) { return TypeFactory::createInt($scalar ? 1 : 0); } elseif (is_string($scalar)) { try { return TypeFactory::createRational($scalar); } catch (\InvalidArgumentException $e) { try { return ComplexTypeFactory::create($scalar); } catch (\InvalidArgumentException $e) { //do nothing } } } throw new ComputationException('Scalar parameter is not a supported type for numeric matrices: ' . getType($scalar)); }
private function toStrongType(array $values, $expectedType) { $ns = '\\Chippyash\\Type\\Number\\'; $class = $ns . $expectedType; $ret = []; foreach ($values as $r => $row) { foreach ($row as $c => $item) { if ($expectedType == 'Rational\\RationalType') { $ret[$r][$c] = RationalTypeFactory::fromFloat($item); } else { $ret[$r][$c] = new $class($item); } } } return $ret; }
/** * @dataProvider luData * */ public function testDecomposeReturnsCorrectResult($source, $LUD, $LD, $UD, $pivotVectorD, $permutationMatrixD, $det) { $mA = new RationalMatrix($source); $LU = new RationalMatrix($LUD); $L = new RationalMatrix($LD); $U = new RationalMatrix($UD); $pivotVector = new RationalMatrix($pivotVectorD); $permutationMatrix = new RationalMatrix($permutationMatrixD); $decomp = $this->object->decompose($mA); // var_dump($decomp->Det);return; $this->assertEquals($LU, $this->object->LU, 'LU matrix incorrect'); $this->assertEquals($L, $this->object->L, 'L matrix incorrect'); $this->assertEquals($U, $this->object->U, 'U matrix incorrect'); $this->assertEquals($pivotVector, $this->object->PivotVector, 'PivotVector matrix incorrect'); $this->assertEquals($permutationMatrix, $this->object->PermutationMatrix, 'Permutation matrix incorrect'); $det = is_null($det) ? null : RTF::create($det); $this->assertEquals($det, $this->object->Det, 'Determinant incorrect'); }
/** * Construct a complete Matrix with all entries set to Chippyash/Type * Takes a source matrix or array (which can be incomplete and converts each * entry to Chippyash/Type), setting a default value if entry does not exist. * * If a NumericMatrix is supplied as $source, the data is cloned into the Matrix * with no further checks. * * @param NumericMatrix|array $source Array to initialise the matrix with * @param mixed $normalizeDefault Value to set missing vertices * @throws \Chippyash\Math\Matrix\Exceptions\MathMatrixException */ public function __construct($source, $normalizeDefault = 0) { if ($source instanceof self) { $this->store($source->toArray()); return; } if (is_array($source)) { if (is_int($normalizeDefault)) { $default = TypeFactory::createInt($normalizeDefault); } elseif (is_float($normalizeDefault)) { $default = RationalTypeFactory::fromFloat($normalizeDefault); } elseif (!$normalizeDefault instanceof NumericTypeInterface) { throw new MathMatrixException('NumericMatrix expects numeric default value'); } else { $default = $normalizeDefault; } parent::__construct($source, false, true, $default); } else { throw new MathMatrixException('NumericMatrix expects NumericMatrix or array as source data'); } }
/** * Construct a square NumericMatrix whose entries on the diagonal == 1, 1/1 or 1+0i * All other entries == 0, 0/1 or 0+0i * * @param IntType $size Number of required rows and columns * @param IntType $identityType Type of identity entries: default == IDM_TYPE_INT * * @throws \InvalidArgumentException */ public function __construct(IntType $size, IntType $identityType = null) { if (is_null($identityType)) { $idt = self::IDM_TYPE_INT; } else { $idt = $identityType(); } if (!in_array($idt, $this->availableTypes)) { throw new \InvalidArgumentException('Identity type invalid'); } if ($size() < 1) { throw new \InvalidArgumentException('size must be >= 1'); } $f = function ($row, $col) use($idt) { if ($idt == self::IDM_TYPE_RATIONAL) { return RationalTypeFactory::create($row == $col ? 1 : 0, 1); } elseif ($idt == self::IDM_TYPE_COMPLEX) { return ComplexTypeFactory::create(RationalTypeFactory::create($row == $col ? 1 : 0, 1), RationalTypeFactory::create(0, 1)); } else { return TypeFactory::createInt($row == $col ? 1 : 0); } }; parent::__construct($f, $size, $size); }
public function nonComplexNumbers() { return [[RationalTypeFactory::create(2, 5)]]; }
protected function toRational(array $dA) { foreach ($dA as &$row) { foreach ($row as &$entry) { if ($entry instanceof NumericTypeInterface) { $entry = $entry->asRational(); } elseif (is_numeric($entry)) { $entry = RationalTypeFactory::fromFloat(floatval($entry)); } else { $entry = $entry; } } } return new Matrix($dA); }
public function computeMatrices() { //set required type as data is generated before tests RequiredType::getInstance()->set(RequiredType::TYPE_NATIVE); return [[[[1, 2, 3], [3, 2, 1], [2, 1, 3]], [[TypeFactory::createInt(2), TypeFactory::createInt(4), TypeFactory::createInt(6)], [TypeFactory::createInt(6), TypeFactory::createInt(4), TypeFactory::createInt(2)], [TypeFactory::createInt(4), TypeFactory::createInt(2), TypeFactory::createInt(6)]], 2], [[[1, 2, 3]], [[RationalTypeFactory::create(2.5), RationalTypeFactory::create(5.0), RationalTypeFactory::create(7.5)]], 2.5], [[[1.5, 2.5, 3.5]], [[RationalTypeFactory::create(3.0), RationalTypeFactory::create(5.0), RationalTypeFactory::create(7.0)]], 2], [[[1.12, 2.12, 3.12]], [[RationalTypeFactory::create(1.12), RationalTypeFactory::create(2.12), RationalTypeFactory::create(3.12)]], 1.0], [[[1, 2, 3]], [[TypeFactory::createInt(1), TypeFactory::createInt(2), TypeFactory::createInt(3)]], true], [[[1, 2, 3]], [[TypeFactory::createInt(0), TypeFactory::createInt(0), TypeFactory::createInt(0)]], false], [[[true, false]], [[TypeFactory::createInt(1), TypeFactory::createInt(0)]], true], [[[true, false]], [[TypeFactory::createInt(0), TypeFactory::createInt(0)]], false]]; }
/** * @runInSeparateProcess */ public function testSetDefaultFromFloatToleranceIsStatic() { RationalTypeFactory::setDefaultFromFloatTolerance(1.0E-5); $this->assertEquals('113/355', (string) RationalTypeFactory::fromFloat(M_1_PI)); RationalTypeFactory::setDefaultFromFloatTolerance(1.0E-15); $this->assertEquals('25510582/80143857', (string) RationalTypeFactory::fromFloat(M_1_PI)); }
public function mixedMatrices() { return [[[[1, 2, 3]], TypeFactory::createInt(6)], [[[1], [2], [3]], TypeFactory::createInt(6)], [[[1, 2, 3], [1, 2, 3]], TypeFactory::createInt(12)], [[[1.1, 2, 3], [1, 2.2, 3]], RationalTypeFactory::fromFloat(12.3)], [[[0.5, 0.5]], RationalTypeFactory::create(1)]]; }
public function testCreateRationalMatrixWithRationalTypeEntriesReturnsRationalMatrix() { $data = [[RF::create(1, -3), RF::create(-4, 6), RF::create(12, 3)]]; $this->assertInstanceOf('Chippyash\\Math\\Matrix\\RationalMatrix', MatrixFactory::createRational($data)); }
public function testMagicInvokeReturnsFloatForRealFloatComplexNumber() { $c = new ComplexType(RationalTypeFactory::fromFloat(2.5), $this->createRationalType(0)); $this->assertInternalType('float', $c()); $this->assertEquals(2.5, $c()); }
public function testSqrtRationalTypeReturnsRationalType() { $res = $this->object->sqrt(RationalTypeFactory::create(7)); $this->assertInstanceOf('\\Chippyash\\Type\\Number\\Rational\\RationalType', $res); $this->assertEquals('46256493/17483311', (string) $res); }
/** * Perform Guass Jordan Elimination on the two supplied matrices * * @param NumericMatrix $mA First matrix to act on - required * @param NumericMatrix $extra Second matrix to act upon - required * * @return \Chippyash\Math\Matrix\DecompositionAbstractDecomposition Fluent Interface * * @throws \Chippyash\Math\Matrix\Exceptions\SingularMatrixException */ public function decompose(NumericMatrix $mA, $extra = null) { $this->assertParameterIsMatrix($extra, 'Parameter extra is not a matrix')->assertMatrixIsNumeric($extra, 'Parameter extra is not a numeric matrix')->assertMatrixIsSquare($mA, 'Parameter mA is not a square matrix')->assertMatrixRowsAreEqual($mA, $extra, 'mA->rows != extra->rows'); $rows = $mA->rows(); $dA = $mA->toArray(); $dB = $extra->toArray(); $zero = function () { return RationalTypeFactory::create(0, 1); }; $one = function () { return RationalTypeFactory::create(1, 1); }; $calc = new Calculator(); $comp = new Comparator(); $ipiv = array_fill(0, $rows, $zero()); $indxr = array_fill(0, $rows, 0); $indxc = array_fill(0, $rows, 0); // find the pivot element by searching the entire matrix for its largest value, but excluding columns already reduced. $irow = $icol = 0; for ($i = 0; $i < $rows; $i++) { $big = $zero(); for ($j = 0; $j < $rows; $j++) { if ($comp->neq($ipiv[$j], $one())) { for ($k = 0; $k < $rows; $k++) { if ($comp->eq($ipiv[$k], $zero())) { $absVal = $dA[$j][$k]->abs(); if ($comp->gt($absVal, $big)) { unset($big); $big = clone $absVal; $irow = $j; $icol = $k; } } elseif ($comp->gt($ipiv[$k], $one())) { throw new SingularMatrixException('GaussJordanElimination'); } } } } //Now interchange row to move the pivot element to a diagonal $ipiv[$icol] = $calc->add($ipiv[$icol], $one()); if ($irow != $icol) { $this->swapRows($dA, $irow, $icol); $this->swapRows($dB, $irow, $icol); } // Remember permutations to later $indxr[$i] = $irow; $indxc[$i] = $icol; if ($comp->eq($dA[$icol][$icol], $zero())) { throw new SingularMatrixException('GaussJordanElimination'); } // Now divide the found row to make the pivot element 1 // To maintain arithmetic integrity we use the reciprocal to multiply by $pivinv = $calc->reciprocal($dA[$icol][$icol]); $this->multRow($dA, $icol, $pivinv, $calc); $this->multRow($dB, $icol, $pivinv, $calc); // And reduce all other rows but the pivoted row with the value of the pivot row for ($ll = 0; $ll < $rows; $ll++) { if ($ll != $icol) { $multiplier = clone $dA[$ll][$icol]; $multiplier->negate(); $this->addMultipleOfOtherRowToRow($dA, $multiplier, $icol, $ll, $calc); $this->addMultipleOfOtherRowToRow($dB, $multiplier, $icol, $ll, $calc); } } } $this->set('left', $this->createCorrectMatrixType($mA, $dA)); $this->set('right', $this->createCorrectMatrixType($extra, $dB)); return clone $this; }
/** * Create complex power from natural base and complex exponent * * @param int|float $base base * @param ComplexType $exp exponent * * @return NI|ComplexType */ private function complexExponent($base, ComplexType $exp) { if ($exp->isReal()) { return $this->rationalPow(RationalTypeFactory::fromFloat($base), $exp->r()); } //do the imaginary part //n^bi = cos(b.lg(n)) + i.sin(b.lg(n)) $b = $exp->i()->get(); $n = log($base); $r = cos($b * $n); $i = sin($b * $n); //no real part if ($exp->r()->get() == 0) { return new ComplexType(RationalTypeFactory::fromFloat($r), RationalTypeFactory::fromFloat($i)); } //real and imaginary part //n^a+bi = n^a(cos(b.lg(n)) + i.sin(b.lg(n))) $na = pow($base, $exp->r()->get()); $rr = $na * $r; $ii = $na * $i; return new ComplexType(RationalTypeFactory::fromFloat($rr), RationalTypeFactory::fromFloat($ii)); }
public function testSingleMatrixReturnsValueOfItsSingleEntry() { $this->assertEquals(2, $this->object->determinant(new NumericMatrix([2]))->get()); $this->assertEquals('2/5', (string) $this->object->determinant(new RationalMatrix([RationalTypeFactory::fromString('2/5')]))); $this->assertEquals('1+3i', $this->object->determinant(new ComplexMatrix([ComplexTypeFactory::fromString('1+3i')]))->get()); }
public function nonComplexNumbers() { return [[2], [-2.4], [new FloatType(2)], [new FloatType(2.6)], [RationalTypeFactory::create(1, 5)], [new WholeIntType(3)], [new NaturalIntType(6)]]; }
/** * Return number as Rational number. * NB, numerator and denominator will be caste as IntTypes * * @return \Chippyash\Type\Number\Rational\RationalType */ public function asRational() { return RationalTypeFactory::fromFloat($this->value); }
/** * Convert to RationalType * * @param mixed $original * * @return \Chippyash\Type\Number\Rational\RationalType|\Chippyash\Type\Number\Rational\GMPRationalType * * @throws InvalidTypeException */ protected static function convertType($original) { if ($original instanceof AbstractRationalType) { return RationalTypeFactory::create($original->numerator()->get(), $original->denominator()->get()); } if (is_numeric($original)) { if (is_int($original)) { return RationalTypeFactory::create($original, 1); } //default - convert to float return RationalTypeFactory::fromFloat(floatval($original)); } if ($original instanceof FloatType) { return RationalTypeFactory::fromFloat($original()); } if ($original instanceof IntType) { return RationalTypeFactory::create($original, 1); } if (is_string($original)) { try { return RationalTypeFactory::fromString($original); } catch (\InvalidArgumentException $e) { throw new InvalidTypeException("{$original} for Complex type construction"); } } $type = gettype($original); throw new InvalidTypeException("{$type} for Complex type construction"); }
public function correctResults() { //set required type as data created before tests are run RequiredType::getInstance()->set(RequiredType::TYPE_NATIVE); return [[1, 2, new IntType(3)], [new IntType(1), 2, new IntType(3)], [1, new IntType(2), new IntType(3)], [new IntType(1), new IntType(2), new IntType(3)], [2.0, 3.0, new FloatType(5.0)], [new FloatType(2.0), 3.0, new FloatType(5.0)], [2.0, new FloatType(3.0), new FloatType(5.0)], [new FloatType(2.0), new FloatType(3.0), new FloatType(5.0)], [new IntType(2), 3.0, new FloatType(5.0)], [new WholeIntType(2), 3, new WholeIntType(5)], [2, new WholeIntType(3), new WholeIntType(5)], [new NaturalIntType(2), 3, new NaturalIntType(5)], [2, new NaturalIntType(3), new NaturalIntType(5)], [RationalTypeFactory::create(4), RationalTypeFactory::create(4), RationalTypeFactory::create(8)]]; }
/** * Create and return a rational number matrix * $data elements are either: * - a RationalType * - string representations of rational number * - a PHP float * - a 2 item array representing numerator & denominator e.g. [2,-4] = '-2/4' * * @param array $data * * @return \Chippyash\Math\Matrix\RationalMatrix * * @throws \Chippyash\Math\Matrix\Exceptions\MathMatrixException */ public static function createRational(array $data) { foreach ($data as &$row) { foreach ($row as &$item) { if (!$item instanceof RationalType) { if (is_array($item) && count($item) == 2) { $item = RationalTypeFactory::create($item[0], $item[1]); } elseif (is_string($item)) { try { $item = RationalTypeFactory::fromString($item); } catch (\InvalidArgumentException $e) { throw new MathMatrixException('Invalid item type for Rational Matrix'); } } elseif (is_float($item)) { $item = RationalTypeFactory::fromFloat($item); } else { throw new MathMatrixException('Invalid item type for Rational Matrix'); } } } } return new RationalMatrix($data); }
public function testReciprocalOfRationalTypeReturnsRationalType() { $this->assertInstanceOf('Chippyash\\Type\\Number\\Rational\\RationalType', $this->object->reciprocal(RationalTypeFactory::create(2, 1))); }
use Chippyash\Type\TypeFactory; use Chippyash\Type\RequiredType; use Chippyash\Type\Number\Rational\RationalTypeFactory; /** * Tuning * set tolerance for creating rationals from floats - sqrt() function will use it. * * Try setting this to the PHP int limit i.e. 1e-17. You will find that some * of the square roots cannot be computed because the limits of the mechanism * to convert floats to rational numbers busts the available precision and therefore * we get into an overflow situation. * * Setting tolerance to a lower number, say 1e-6, will compute faster but at the * expense of accuracy */ RationalTypeFactory::setDefaultFromFloatTolerance(1.0E-15); /** * Set the required number type. System will automatically use GMP if * it is available. You can force it to use native PHP thus: */ RequiredType::getInstance()->set(RequiredType::TYPE_NATIVE); //now create 10000 numbers for the test //try playing with this figure to see the results $numbers = []; for ($x = 1; $x < 10001; $x++) { $numbers[$x] = TypeFactory::create('int', $x); } //create primes $primes = []; $start = microtime(true); foreach ($numbers as $key => $number) {
/** * Return the angle (sometimes known as the argument) of the number * when expressed in polar notation * * The return value is a rational expressing theta as radians * * @return \Chippyash\Type\Number\Rational\RationalType */ public function theta() { return RationalTypeFactory::fromFloat(atan2($this->value['imaginary']->asFloatType()->get(), $this->value['real']->asFloatType()->get())); }