PoslavskySV / rings

Rings: efficient JVM library for polynomial rings
https://rings.readthedocs.io
72 stars 10 forks source link

Failing to serialize Rational #61

Closed tueda closed 5 years ago

tueda commented 5 years ago

Though Rational is Serializable, when I try to serialize a Rational object with Rings v2.5.2, it fails:

import java.io.*
import cc.redberry.rings.*
import cc.redberry.rings.poly.multivar.*

var ring = Rings.MultivariateRing(3, Rings.Z)
var field = Rings.Frac(ring)

var p = MultivariatePolynomial.parse("1+x+y", "x", "y")
var q = MultivariatePolynomial.parse("1+x-y", "x", "y")
var r = field.mk(p, q)

var bstream = new ByteArrayOutputStream()
var ostream = new ObjectOutputStream(bstream)

ostream.writeObject(r)
jshell> ostream.writeObject(r)
|  Exception java.io.NotSerializableException: cc.redberry.rings.Rational$$Lambda$18/0x000000084007f440
|        at ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1185)
|        at ObjectOutputStream.defaultWriteFields (ObjectOutputStream.java:1553)
|        at ObjectOutputStream.writeSerialData (ObjectOutputStream.java:1510)
|        at ObjectOutputStream.writeOrdinaryObject (ObjectOutputStream.java:1433)
|        at ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1179)
|        at ObjectOutputStream.writeObject (ObjectOutputStream.java:349)
|        at (#11:1)

I guess simplicityCriteria would be not Serializable and could be transient...? https://github.com/PoslavskySV/rings/blob/f156e02e86781c0ace65a6b3d462f0313ca087bc/rings/src/main/java/cc/redberry/rings/Rational.java#L36

PoslavskySV commented 5 years ago

@tueda Hi Takahiro! Thanks for reporting this. The bug was already fixed in the develop branch, so now I just made a release (2.5.3) with the fix.

Nevertheless I won't recommend using Java's serialization in Rings: it is much slower than e.g. encoding/decoding expressions from strings. So, for serialization I would recommend using Rings's Coder (see this):

import java.io.*
import cc.redberry.rings.*
import cc.redberry.rings.poly.multivar.*

var ring = Rings.MultivariateRing(3, Rings.Z)
var field = Rings.Frac(ring)

var p = MultivariatePolynomial.parse("1+x+y", "x", "y")
var q = MultivariatePolynomial.parse("1+x-y", "x", "y")
var r = field.mk(p, q)

var mCoder = Coder.mkMultivariateCoder(ring, "x", "y")
Coder<Rational<MultivariatePolynomial<BigInteger>>, ?, ?> coder = Coder.mkRationalsCoder(field, mCoder)

coder.encode(r) // to string

assert r.equals(coder.decode(coder.encode(r)))
tueda commented 5 years ago

Thanks for the prompt response!

It seems that Rings.Z must be a singleton (or all instances of Integers should be "equal") and the serialization breaks it, so if I try to, for example, add an object obtained by deserialization and a newly created object, then easily get

import cc.redberry.rings.*
import cc.redberry.rings.poly.multivar.*

var ring = Rings.MultivariateRing(2, Rings.Z)
var field = Rings.Frac(ring)

var p = MultivariatePolynomial.parse("1+x+y", "x", "y")
var q = MultivariatePolynomial.parse("1+x-y", "x", "y")
var r = field.mk(p, q)

var bstream1 = new ByteArrayOutputStream()
var ostream1 = new ObjectOutputStream(bstream1)

ostream1.writeObject(r)
ostream1.close()

var data = bstream1.toByteArray()

var bstream2 = new ByteArrayInputStream(data)
var ostream2 = new ObjectInputStream(bstream2)

var t = (Rational<MultivariatePolynomial<cc.redberry.rings.bigint.BigInteger>>) ostream2.readObject()

t.ring.equals(r.ring)  // gives false

t.add(r)
jshell> t.add(r)
|  Exception java.lang.IllegalArgumentException: Mixing polynomials over different coefficient rings: Z and Z
|        at IPolynomial.assertSameCoefficientRingWith (IPolynomial.java:36)
|        at AMultivariatePolynomial.add (AMultivariatePolynomial.java:1190)
|        at AMultivariatePolynomial.add (AMultivariatePolynomial.java:76)
|        at APolynomialRing.add (APolynomialRing.java:38)
|        at APolynomialRing.add (APolynomialRing.java:12)
|        at Rational.add (Rational.java:588)
|        at (#17:1)

But if encoding/decoding expressions via string is faster than Java's serialization, then I can use it. I haven't tried for such performance comparison, it's worth information.

(Though encoding/decoding via string is better, should I keep this issue open for the Rings.Z serialization problem?)

PoslavskySV commented 5 years ago

Yes, you are right, it was a problem with Rings.Z --- I just fixed it in develop. In any case I recommend you to use Coder. Please, let me know if I can help!

tueda commented 5 years ago

The Corder works very well, regardless of the Rings.Z problem. Just for information, in jshell:

import cc.redberry.rings.*
import cc.redberry.rings.io.*
import cc.redberry.rings.poly.multivar.*

var ring = Rings.MultivariateRing(2, Rings.Z)
var field = Rings.Frac(ring)

var p = MultivariatePolynomial.parse("1+x+y", "x", "y")
var q = MultivariatePolynomial.parse("1+x-y", "x", "y")
var r = field.mk(p, q)

var mCoder = Coder.mkMultivariateCoder(ring, "x", "y")
var coder = Coder.mkRationalsCoder(field, mCoder)

var s = coder.encode(r)
var t = coder.decode(s)

r.equals(t)  // gives true
r.add(t)  // works well

I have checked the develop branch, and confirmed the Rings.Z problem was fixed: the code in the previous post now works. Thanks again!