Difference between the IS-A and Liskov Substitution Principle?

Both terms describe the same "concept" in the end.

The Liskov substitution principle tells you: an inheritance relation between to classes B (base) and C (child) is sound when each and any usage of some object of type B ... can be replaced with an object of type C.

This means: B defines an API and a public contract - and C has to uphold these properties, too!

And IS-A amounts to the same thing: that some object of C is also a B.

The "difference" is: the LSP gives you exact rules that you can check. Whereas IS-A is more of an "observation" or expression of intent. Like in: you express that you wish that class C IS-A B.

In other words: when you don't know how to properly use inheritance, IS-A doesn't help you writing correct code. Whereas the LSP clearly tells you that something like:

class Base { int foo(); }
class Child extends Base { @Override double foo(); }

is invalid. According to LSP, you can only widen method arguments, and restrict return values.

int iValue = someBase.foo();

can't be replaced with

int iValue = someChild.foo();

because the foo() method is widened in its result.

And a final thought: many people think that C IS-A B is the same as just writing down Child extends Base. Yes. But this only tells the compiler that C extends B. It doesn't mean that the methods you use in C will follow the LSP and thus turn C into a real valid descendant of B.

C IS-A B requires more than "C extends B". To be truly valid, LSP has to be uphold!