Skip to content
This repository has been archived by the owner on Feb 8, 2022. It is now read-only.

Add BoolRng and GenBool. Resolves #107. #109

Merged
merged 2 commits into from
Nov 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions core/src/main/scala/algebra/lattice/Bool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ import scala.{specialized => sp}
* Every boolean algebras has a dual algebra, which involves reversing
* true/false as well as and/or.
*/
trait Bool[@sp(Int, Long) A] extends Any with Heyting[A] { self =>
trait Bool[@sp(Int, Long) A] extends Any with Heyting[A] with GenBool[A] { self =>
def imp(a: A, b: A): A = or(complement(a), b)
def without(a: A, b: A): A = and(a, complement(b))

// xor is already defined in both Heyting and GenBool.
// In Bool, the definitions coincide, so we just use one of them.
override def xor(a: A, b: A): A = super.xor(a, b)

override def dual: Bool[A] = new DualBool(this)

Expand All @@ -37,14 +42,7 @@ trait Bool[@sp(Int, Long) A] extends Any with Heyting[A] { self =>
* Note that the ring returned by this method is not an extension of
* the `Rig` returned from [[BoundedDistributiveLattice.asCommutativeRig]].
*/
def asBoolRing: BoolRing[A] =
new BoolRing[A] {
def zero: A = self.zero
def one: A = self.one
def plus(x: A, y: A): A = self.xor(x, y)
def times(x: A, y: A): A = self.and(x, y)
override def asBool: Bool[A] = self
}
override def asBoolRing: BoolRing[A] = new BoolRingFromBool(self)
}

class DualBool[@sp(Int, Long) A](orig: Bool[A]) extends Bool[A] {
Expand All @@ -63,6 +61,11 @@ class DualBool[@sp(Int, Long) A](orig: Bool[A]) extends Bool[A] {
override def dual: Bool[A] = orig
}

private[lattice] class BoolRingFromBool[A](orig: Bool[A]) extends BoolRngFromGenBool(orig) with BoolRing[A] {
def one: A = orig.one
override def asBool: Bool[A] = orig
}

trait BoolFunctions {
def dual[@sp(Int, Long) A](implicit ev: Bool[A]): Bool[A] =
ev.dual
Expand Down
57 changes: 57 additions & 0 deletions core/src/main/scala/algebra/lattice/GenBool.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package algebra
package lattice

import ring.BoolRng
import scala.{specialized => sp}

/**
* Generalized Boolean algebra, that is, a Boolean algebra without
* the top element. Generalized Boolean algebras do not (in general)
* have (absolute) complements, but they have ''relative complements''
* (see [[GenBool.without]]).
*/
trait GenBool[@sp(Int, Long) A] extends Any with DistributiveLattice[A] with BoundedJoinSemilattice[A] { self =>
def and(a: A, b: A): A
override def meet(a: A, b: A): A = and(a, b)

def or(a: A, b: A): A
override def join(a: A, b: A): A = or(a, b)

/**
* The operation of ''relative complement'', symbolically often denoted
* `a\b` (the symbol for set-theoretic difference, which is the
* meaning of relative complement in the lattice of sets).
*/
def without(a: A, b: A): A

/**
* Logical exclusive or, set-theoretic symmetric difference.
* Defined as `a\b ∨ b\a`.
*/
def xor(a: A, b: A): A = or(without(a, b), without(b, a))

/**
* Every generalized Boolean algebra is also a `BoolRng`, with
* multiplication defined as `and` and addition defined as `xor`.
*/
def asBoolRing: BoolRng[A] = new BoolRngFromGenBool(self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asBoolRng, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional, because I didn't want to introduce a new method name.
It is kind of analogous to dual, where the subtype overrides the return
type.
On Oct 30, 2015 9:25 PM, "P. Oscar Boykin" [email protected] wrote:

In core/src/main/scala/algebra/lattice/GenBool.scala
#109 (comment):

  • * a\b (the symbol for set-theoretic difference, which is the
  • * meaning of relative complement in the lattice of sets).
  • */
  • def without(a: A, b: A): A
  • /**
  • * Logical exclusive or, set-theoretic symmetric difference.
  • * Defined as a\b ∨ b\a.
  • */
  • def xor(a: A, b: A): A = or(without(a, b), without(b, a))
  • /**
  • * Every generalized Boolean algebra is also a BoolRng, with
  • * multiplication defined as and and addition defined as xor.
  • */
  • def asBoolRing: BoolRng[A] = new BoolRngFromGenBool(self)

asBoolRng, right?


Reply to this email directly or view it on GitHub
https://github.com/non/algebra/pull/109/files#r43566192.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we change both method names to BoolRng? It seems a bit strange to name it for the more derived case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make sense. Or maybe to asRingLike? Similarly asBool to
asGenBool/asBoolLike?
On Oct 30, 2015 9:45 PM, "P. Oscar Boykin" [email protected] wrote:

In core/src/main/scala/algebra/lattice/GenBool.scala
#109 (comment):

  • * a\b (the symbol for set-theoretic difference, which is the
  • * meaning of relative complement in the lattice of sets).
  • */
  • def without(a: A, b: A): A
  • /**
  • * Logical exclusive or, set-theoretic symmetric difference.
  • * Defined as a\b ∨ b\a.
  • */
  • def xor(a: A, b: A): A = or(without(a, b), without(b, a))
  • /**
  • * Every generalized Boolean algebra is also a BoolRng, with
  • * multiplication defined as and and addition defined as xor.
  • */
  • def asBoolRing: BoolRng[A] = new BoolRngFromGenBool(self)

Should we change both method names to BoolRng? It seems a bit strange to
name it for the more derived case.


Reply to this email directly or view it on GitHub
https://github.com/non/algebra/pull/109/files#r43566532.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we settle #108 first? Because, my first preference is actually to remove these methods and make the classes you wrote public.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, maybe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this PR is basically done. Let's merge this and not block it on #108. If we remove constructions, this is a marginal amount compared with what we have already.

}

private[lattice] class BoolRngFromGenBool[@sp(Int, Long) A](orig: GenBool[A]) extends BoolRng[A] {
def zero: A = orig.zero
def plus(x: A, y: A): A = orig.xor(x, y)
def times(x: A, y: A): A = orig.and(x, y)
override def asBool: GenBool[A] = orig
}

trait GenBoolFunctions {
def zero[@sp(Int, Long) A](implicit ev: GenBool[A]): A = ev.zero
def and[@sp(Int, Long) A](x: A, y: A)(implicit ev: GenBool[A]): A = ev.and(x, y)
def or[@sp(Int, Long) A](x: A, y: A)(implicit ev: GenBool[A]): A = ev.or(x, y)
def without[@sp(Int, Long) A](x: A, y: A)(implicit ev: GenBool[A]): A = ev.without(x, y)
def xor[@sp(Int, Long) A](x: A, y: A)(implicit ev: GenBool[A]): A = ev.xor(x, y)
}

object GenBool extends BoolFunctions {
@inline final def apply[@sp(Int, Long) A](implicit ev: GenBool[A]): GenBool[A] = ev
}
19 changes: 9 additions & 10 deletions core/src/main/scala/algebra/ring/BoolRing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import lattice.Bool
* Every Boolean ring is equivalent to a Boolean algebra.
* See [[BoolRing.asBool]] for details.
*/
trait BoolRing[A] extends Any with CommutativeRing[A] { self =>
override final def negate(x: A): A = x
trait BoolRing[A] extends Any with BoolRng[A] with CommutativeRing[A] { self =>

/**
* Every Boolean ring gives rise to a Boolean algebra:
Expand All @@ -26,14 +25,14 @@ trait BoolRing[A] extends Any with CommutativeRing[A] { self =>
*
* @see [[algebra.lattice.Bool.asBoolRing]]
*/
def asBool: Bool[A] = new Bool[A] {
def zero: A = self.zero
def one: A = self.one
def and(a: A, b: A): A = self.times(a, b)
def complement(a: A): A = self.plus(self.one, a)
def or(a: A, b: A): A = self.plus(self.plus(a, b), self.times(a, b))
override def asBoolRing: BoolRing[A] = self
}
override def asBool: Bool[A] = new BoolFromBoolRing(self)
}

private[ring] class BoolFromBoolRing[A](orig: BoolRing[A]) extends GenBoolFromBoolRng(orig) with Bool[A] {
def one: A = orig.one
def complement(a: A): A = orig.plus(orig.one, a)
override def without(a: A, b: A): A = super[GenBoolFromBoolRng].without(a, b)
override def asBoolRing: BoolRing[A] = orig
}

object BoolRing extends RingFunctions {
Expand Down
42 changes: 42 additions & 0 deletions core/src/main/scala/algebra/ring/BoolRng.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package algebra
package ring

import lattice.GenBool

/**
* A Boolean rng is a rng whose multiplication is idempotent, that is
* `a⋅a = a` for all elements ''a''. This property also implies `a+a = 0`
* for all ''a'', and `a⋅b = b⋅a` (commutativity of multiplication).
*
* Every `BoolRng` is equivalent to [[algebra.lattice.GenBool]].
* See [[BoolRng.asBool]] for details.
*/
trait BoolRng[A] extends Any with Rng[A] { self =>
override final def negate(x: A): A = x

/**
* Every Boolean rng gives rise to a Boolean algebra without top:
* - 0 is preserved;
* - ring multiplication (`times`) corresponds to `and`;
* - ring addition (`plus`) corresponds to `xor`;
* - `a or b` is then defined as `a xor b xor (a and b)`;
* - relative complement `a\b` is defined as `a xor (a and b)`.
*
* `BoolRng.asBool.asBoolRing` gives back the original `BoolRng`.
*
* @see [[algebra.lattice.GenBool.asBoolRing]]
*/
def asBool: GenBool[A] = new GenBoolFromBoolRng(self)
}

private[ring] class GenBoolFromBoolRng[A](orig: BoolRng[A]) extends GenBool[A] {
def zero: A = orig.zero
def and(a: A, b: A): A = orig.times(a, b)
def or(a: A, b: A): A = orig.plus(orig.plus(a, b), orig.times(a, b))
def without(a: A, b: A): A = orig.plus(a, orig.times(a, b))
override def asBoolRing: BoolRng[A] = orig
}

object BoolRng extends AdditiveGroupFunctions with MultiplicativeSemigroupFunctions {
@inline final def apply[A](implicit r: BoolRng[A]): BoolRng[A] = r
}
27 changes: 22 additions & 5 deletions laws/shared/src/main/scala/algebra/laws/LogicLaws.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package algebra.laws

import algebra._
import algebra.lattice.{Heyting, Bool}
import algebra.lattice.{Heyting, Bool, GenBool}

import org.typelevel.discipline.Laws

Expand All @@ -19,7 +19,7 @@ trait LogicLaws[A] extends LatticeLaws[A] {

def heyting(implicit A: Heyting[A]) = new LogicProperties(
name = "heyting",
parent = None,
parents = Seq(),
ll = boundedDistributiveLattice,

Rules.distributive(A.or)(A.and),
Expand Down Expand Up @@ -56,20 +56,37 @@ trait LogicLaws[A] extends LatticeLaws[A] {
"(0 → x) = 1" -> forAll { (x: A) => A.imp(A.zero, x) ?== A.one }
)

def generalizedBool(implicit A: GenBool[A]) = new LogicProperties(
name = "generalized bool",
parents = Seq(),
ll = new LatticeProperties(
name = "lowerBoundedDistributiveLattice",
parents = Seq(boundedJoinSemilattice, distributiveLattice),
join = Some(boundedSemilattice(A.joinSemilattice)),
meet = Some(semilattice(A.meetSemilattice))
),

"""x\y ∧ y = 0""" -> forAll { (x: A, y: A) =>
A.and(A.without(x, y), y) ?== A.zero },

"""x\y ∨ y = x ∨ y""" -> forAll { (x: A, y: A) =>
A.or(A.without(x, y), y) ?== A.or(x, y) }
)

def bool(implicit A: Bool[A]) = new LogicProperties(
name = "bool",
parent = Some(heyting),
parents = Seq(heyting, generalizedBool),
ll = boundedDistributiveLattice,

"excluded middle" -> forAll { (x: A) => A.or(x, A.complement(x)) ?== A.one }
)

class LogicProperties(
val name: String,
val parent: Option[LogicProperties],
val parents: Seq[LogicProperties],
val ll: LatticeProperties,
val props: (String, Prop)*
) extends RuleSet with HasOneParent {
) extends RuleSet {
val bases = Seq("lattice" -> ll)
}

Expand Down
6 changes: 6 additions & 0 deletions laws/shared/src/main/scala/algebra/laws/RingLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ trait RingLaws[A] extends GroupLaws[A] {
parents = Seq(ring, commutativeRig)
)

def boolRng(implicit A: BoolRng[A]) = RingProperties.fromParent(
name = "boolean rng",
parent = rng,
Rules.idempotence(A.times)
)

def boolRing(implicit A: BoolRing[A]) = RingProperties.fromParent(
name = "boolean ring",
parent = commutativeRing,
Expand Down
6 changes: 4 additions & 2 deletions laws/shared/src/test/scala/algebra/laws/LawTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ trait LawTestsBase extends FunSuite with Discipline {
laws[OrderLaws, List[String]].check(_.order)
laws[GroupLaws, List[String]].check(_.monoid)

laws[LatticeLaws, Set[Int]].check(_.distributiveLattice)
laws[LatticeLaws, Set[Int]].check(_.boundedJoinLattice)
laws[LogicLaws, Set[Byte]].check(_.generalizedBool)
laws[RingLaws, Set[Byte]].check(_.boolRng(setBoolRng[Byte]))
laws[LogicLaws, Set[Byte]]("bool-from-rng").check(_.generalizedBool(setBoolRng.asBool))
laws[RingLaws, Set[Byte]]("rng-from-bool").check(_.boolRng(GenBool[Set[Byte]].asBoolRing))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched from Set[Int] to Set[Byte] in order to have more exhaustive coverage of the domain.

laws[OrderLaws, Set[Int]].check(_.partialOrder)
laws[RingLaws, Set[Int]].check(_.semiring)
laws[RingLaws, Set[String]].check(_.semiring)
Expand Down
20 changes: 14 additions & 6 deletions std/src/main/scala/algebra/std/set.scala
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package algebra
package std

import algebra.lattice.{BoundedJoinSemilattice, DistributiveLattice}
import algebra.ring.Semiring
import algebra.lattice.GenBool
import algebra.ring.{BoolRng, Semiring}

package object set extends SetInstances

trait SetInstances {
implicit def setLattice[A]: DistributiveLattice[Set[A]] with BoundedJoinSemilattice[Set[A]] = new SetLattice[A]
implicit def setLattice[A]: GenBool[Set[A]] = new SetLattice[A]
implicit def setPartialOrder[A]: PartialOrder[Set[A]] = new SetPartialOrder[A]
implicit def setSemiring[A] = new SetSemiring[A]
def setBoolRng[A] = new SetBoolRng[A]
}

class SetLattice[A] extends DistributiveLattice[Set[A]] with BoundedJoinSemilattice[Set[A]] {
class SetLattice[A] extends GenBool[Set[A]] {
def zero: Set[A] = Set.empty[A]
def join(lhs: Set[A], rhs: Set[A]): Set[A] = lhs.union(rhs)
def meet(lhs: Set[A], rhs: Set[A]): Set[A] = lhs.intersect(rhs)
def or(lhs: Set[A], rhs: Set[A]): Set[A] = lhs.union(rhs)
def and(lhs: Set[A], rhs: Set[A]): Set[A] = lhs.intersect(rhs)
def without(lhs: Set[A], rhs: Set[A]): Set[A] = lhs -- rhs
}

class SetPartialOrder[A] extends PartialOrder[Set[A]] {
Expand All @@ -33,3 +35,9 @@ class SetSemiring[A] extends Semiring[Set[A]] {
def plus(x: Set[A], y: Set[A]): Set[A] = x | y
def times(x: Set[A], y: Set[A]): Set[A] = x & y
}

class SetBoolRng[A] extends BoolRng[Set[A]] {
def zero: Set[A] = Set.empty
def plus(x: Set[A], y: Set[A]): Set[A] = (x--y) | (y--x)
def times(x: Set[A], y: Set[A]): Set[A] = x & y
}